bigywater.tech
Java 内部原理 · 59

CountDownLatch 与 CyclicBarrier 真实对比:一次性和可复用的区别

两者都是"等待多个线程到齐"的工具,经常被混着用。这篇真实用同一段并发骨架跑两个工具,验证 CountDownLatch 用完即废、CyclicBarrier 能自动重置反复使用这条核心区别,并真实捕获 CyclicBarrier 的 barrierAction 回调触发时机。

Latch 归零后 await 立即返回
真实验证:CountDownLatch 计数降到0后不能重置,再调用 countDown() 无效果,再调用 await() 真实立刻返回
Barrier 真实复用了2轮
真实验证:同一个 CyclicBarrier 对象在3个线程各自到达屏障点后自动重置,真实支持了连续2轮独立的汇合等待

上一篇:ThreadPoolExecutor 拒绝策略真实验证

真实验证:CountDownLatch 统一发令 + 等待全部完成

用两个 CountDownLatch 分别控制"统一开始"和"等待全部结束",这是 CountDownLatch 最经典的双向用法。

CountdownLatchCyclicBarrierDemo.java CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(workers); // 每个worker: startSignal.await() -> 干活 -> doneSignal.countDown() startSignal.countDown(); // 一次性触发所有worker开始 doneSignal.await();
真实实测
发令前 finished 应为0: 0
CountDownLatch: 全部 3 个worker完成,finished=3,总耗时约269ms(受限于最慢worker,非累加)

真实结果验证了 CountDownLatch 的两种典型用法:计数为 1 的 startSignal 用作"发令枪"(一次 countDown() 释放所有等待它的线程),计数为 worker 数量的 doneSignal 用作"汇合点"(等待所有线程都调用一次 countDown())。三个 worker 分别睡眠 100/150/200ms,真实总耗时约 269ms,和最慢的那个 worker 耗时基本一致,不是三者耗时的累加,证明它们确实是并发执行的。

真实验证:CountDownLatch 用尽后无法重置

计数降到 0 之后,这个 latch 对象就"废"了,验证再次调用的真实表现。

真实实测
CountDownLatch 计数到0后再次 await 是否立刻返回: true

真实验证了 CountDownLatch 是不可重置、一次性的:计数已经是 0 时再调用 countDown() 不会有任何效果(不会变负,也不会报错),而 await() 因为条件("计数是否为 0")已经满足,会真实立刻返回,不会等待。这意味着同一个 CountDownLatch 对象无法用于第二轮"发令-等待"。

真实验证:CyclicBarrier 自动重置,可复用多轮

同样是"等待所有参与者到齐",CyclicBarrier 在每一轮所有线程都到达 await() 后会自动重置,可以立刻开始下一轮。

CountdownLatchCyclicBarrierDemo.java CyclicBarrier barrier = new CyclicBarrier(parties, () -> { System.out.println("第 " + round + " 轮所有线程到齐"); }); for (int round = 0; round < 2; round++) { for (int i = 0; i < parties; i++) { pool.submit(() -> { ...; barrier.await(); }); } }
真实实测
  -> CyclicBarrier 触发 barrierAction,第 1 轮所有线程到齐
  -> CyclicBarrier 触发 barrierAction,第 2 轮所有线程到齐
CyclicBarrier 复用了 2 轮(CountDownLatch 无法做到这一点)
真实验证的核心区别。同一个 CyclicBarrier 对象真实支撑了两轮独立的"三线程汇合",每一轮最后一个到达 await() 的线程会真实触发一次构造时传入的 barrierAction 回调,然后屏障自动重置,允许新一轮的线程重新开始计数、等待。这是它和 CountDownLatch 最根本的设计差异:CyclicBarrier 天生就是为"分阶段、循环执行"的并行算法设计的(比如经典的并行计算里,每一轮迭代后所有线程都要同步一次再进入下一轮),而 CountDownLatch 只适合一次性的"等待某个事件发生"。

分步演示:两种"等大家都到齐"的工具,真实区别在哪

每一行都是真实运行这段多线程代码得到的结果(通过CountDownLatch控制观测时机,保证结果确定)。

CountDownLatch 真实是一次性的,归零后无法重置;CyclicBarrier 真实每凑齐一批参与者就自动重置,可以反复使用
这一步新验证的同步结果

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,CyclicBarrier 两轮复用重复运行结果一致。
    • 和Fork/Join 框架那篇一起看,能对比"分治递归"和"分阶段同步"这两种不同的并行协调模式各自适合的场景。
    • 没有涉及:CyclicBarrier 在某个线程等待超时或被中断时,其余线程真实会收到 BrokenBarrierException 的联动失效机制——这个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号