CyclicBarrier和CountDownLatch
CyclicBarrier和CountDownLatch
这两个类都在jdk的并发包中,都可以用来表示代码运行到某个点上
两者的区别
- CyclicBarrier表示达到一定数量的线程才会运行;CountDownLatch每来一个线程进行减一操作,直到0为止
- CyclicBarrier只能唤起一个任务;CountDownLatch可以唤起多个任务
- CyclicBarrier可重用;CountDownLatch不可重用,只能触发一次事件,值为0后就不可再用了
- CyclicBarrier允许N个线程相互等待;CountDownLatch是允许1或N个线程等待其他线程完成执行
核心区别对比表
| 特性 | CyclicBarrier | CountDownLatch |
|---|---|---|
| 计数器机制 | 初始值为 parties,每次 await() 减 1,归 0 后自动重置 |
初始值为 count,每次 countDown() 减 1,归 0 后不可用 |
| 线程协作模式 | N 个线程互相等待,全部到达屏障后继续执行 | 1 个(或多个)线程等待其他线程完成操作 |
| 重用性 | 支持循环使用(通过 reset() 或自动重置) |
一次性使用,计数器归 0 后无法重置 |
| 屏障动作 | 支持 Runnable 任务,在所有线程到达后执行 |
无特殊动作,仅唤醒等待线程 |
| 典型场景 | 并行计算的多阶段同步(如 MapReduce) | 主线程等待多个子线程完成初始化 |
代码示例对比
CyclicBarrier:多线程互相等待
public class CyclicBarrierExample {
public static void main(String[] args) {
int parties = 3;
CyclicBarrier barrier = new CyclicBarrier(parties,
() -> System.out.println("所有线程到达屏障,开始下一步操作"));
for (int i = 0; i < parties; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 准备就绪");
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName() + " 继续执行");
} catch (Exception e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
}
// 输出示例:
// Thread-0 准备就绪
// Thread-1 准备就绪
// Thread-2 准备就绪
// 所有线程到达屏障,开始下一步操作
// Thread-2 继续执行
// Thread-1 继续执行
// Thread-0 继续执行
CountDownLatch:主线程等待子线程
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int count = 3;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 开始工作");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 完成工作");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 工作完成,计数器减1
}
}, "Worker-" + i).start();
}
System.out.println("主线程等待所有工作线程完成...");
latch.await(); // 阻塞主线程,直到计数器为0
System.out.println("主线程继续执行");
}
}
// 输出示例:
// Worker-0 开始工作
// Worker-1 开始工作
// Worker-2 开始工作
// 主线程等待所有工作线程完成...
// (约1秒后)
// Worker-0 完成工作
// Worker-1 完成工作
// Worker-2 完成工作
// 主线程继续执行
深入理解:应用场景选择
CyclicBarrier 适用场景
- 并行计算多阶段任务:如多线程计算矩阵乘法,每个线程负责一行 / 列,全部计算完成后汇总结果。
- 游戏 / 模拟启动:多个玩家线程必须全部准备好后,游戏才能开始。
CountDownLatch 适用场景
- 服务启动依赖:主服务等待所有子服务(如数据库、网络、缓存)初始化完成后再启动。
- 并发性能测试:主线程等待所有工作线程就绪后,同时触发请求以测试并发性能。
总结
- 选 CyclicBarrier:当需要多个线程反复在某个点同步,且希望自动触发后续操作时。
- 选 CountDownLatch:当只需要一次性等待其他线程完成操作,且不需要自动触发任务时。