三大武器 助你玩转并发编程
2023-05-23 01:51:27
并发编程中的协调机制:CountDownLatch、CyclicBarrier 和 Semaphore
引言
在多线程编程中,协调线程的执行至关重要。Java 为我们提供了三个强大的辅助类:CountDownLatch、CyclicBarrier 和 Semaphore,它们可以帮助我们简化线程之间的通信和同步。
CountDownLatch:等待所有线程完成
CountDownLatch 是一种等待机制,它允许一个线程等待其他多个线程完成执行后才继续执行。就像一个倒计时器,CountDownLatch 初始化时会指定一个计数,表示需要等待的线程数量。每个线程完成任务后,调用 CountDownLatch 的 countDown() 方法递减这个计数。当计数减到 0 时,等待的线程就会继续执行。
CountDownLatch latch = new CountDownLatch(3);
// 启动 3 个线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
// 等待 3 个线程完成
latch.await();
// 继续执行主线程
CyclicBarrier:等待所有线程到达屏障
CyclicBarrier 类似于 CountDownLatch,但它允许线程在到达某个屏障后继续执行,而不需要等待其他线程完成执行。与 CountDownLatch 不同,CyclicBarrier 可以被多次重置,允许线程在多个屏障之间协调。
CyclicBarrier barrier = new CyclicBarrier(3);
// 启动 3 个线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
barrier.await();
}).start();
}
// 等待 3 个线程到达屏障
barrier.await();
// 继续执行主线程
Semaphore:控制共享资源访问
Semaphore 是一种用于控制同时访问共享资源的线程数量的机制。它允许我们指定一个许可证数量,表示可以同时访问共享资源的线程数量。线程需要访问共享资源时,调用 Semaphore 的 acquire() 方法获取许可证。如果许可证不足,线程会阻塞等待,直到有许可证可用。当线程访问共享资源完毕后,调用 Semaphore 的 release() 方法释放许可证。
Semaphore semaphore = new Semaphore(3);
// 启动 10 个线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 尝试获取许可证
semaphore.acquire();
// 访问共享资源
// 释放许可证
semaphore.release();
}).start();
}
比较
特性 | CountDownLatch | CyclicBarrier | Semaphore |
---|---|---|---|
用途 | 等待所有线程完成 | 等待所有线程到达屏障 | 控制共享资源访问 |
重置 | 不可重置 | 可重置 | 不可重置 |
线程状态 | 阻塞 | 阻塞 | 阻塞 |
结论
CountDownLatch、CyclicBarrier 和 Semaphore 是并发编程中不可或缺的工具,它们可以帮助我们协调线程的执行,防止数据竞争,并确保线程安全的代码。熟练掌握这些辅助类的用法将极大地提高我们的并发编程能力。
常见问题解答
-
CountDownLatch 和 CyclicBarrier 的区别是什么?
CountDownLatch 用于等待所有线程完成执行,而 CyclicBarrier 用于等待所有线程到达某个屏障。 -
Semaphore 有什么特殊用途?
Semaphore 用于控制同时访问共享资源的线程数量,防止数据竞争。 -
如何防止使用 CountDownLatch 时发生死锁?
确保所有线程最终都会调用 countDown() 方法,以避免死锁。 -
CyclicBarrier 可以多次使用吗?
是的,CyclicBarrier 可以被多次重置,允许线程在多个屏障之间协调。 -
Semaphore 与锁有什么区别?
Semaphore 是基于许可证的机制,而锁是基于排他访问的机制。Semaphore 允许多个线程同时访问共享资源,只要许可证足够,而锁只允许一个线程在同一时间访问共享资源。