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:多线程互相等待
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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(); } } }
|
CountDownLatch:主线程等待子线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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(); } }, "Worker-" + i).start(); }
System.out.println("主线程等待所有工作线程完成..."); latch.await(); System.out.println("主线程继续执行"); } }
|
深入理解:应用场景选择
CyclicBarrier 适用场景
- 并行计算多阶段任务:如多线程计算矩阵乘法,每个线程负责一行 / 列,全部计算完成后汇总结果。
- 游戏 / 模拟启动:多个玩家线程必须全部准备好后,游戏才能开始。
CountDownLatch 适用场景
- 服务启动依赖:主服务等待所有子服务(如数据库、网络、缓存)初始化完成后再启动。
- 并发性能测试:主线程等待所有工作线程就绪后,同时触发请求以测试并发性能。
总结
- 选 CyclicBarrier:当需要多个线程反复在某个点同步,且希望自动触发后续操作时。
- 选 CountDownLatch:当只需要一次性等待其他线程完成操作,且不需要自动触发任务时。
v1.3.10