countdownlatch不可重用而cyclicbarrier可重用,根本区别在于设计意图:前者用于一次性等待所有任务完成,后者适用于多轮同步协作。

CountDownLatch 不可重用,CyclicBarrier 可重用 —— 这是选择的分水岭,不是语法差异,而是设计意图的根本区别。
CountDownLatch 适合“等全部做完再走”这类一次性收尾
它本质是倒计时门禁:构造时传入线程数(比如 new CountDownLatch(5)),每个线程完成就调用 countDown(),主线程或某个协调线程调用 await() 阻塞等待。一旦计数归零,所有 await() 立即返回,且该实例彻底失效。
常见错误现象:
- 误在循环中反复调用同一个
CountDownLatch的await(),结果第二次就直接跳过(因计数已为 0)或抛IllegalMonitorStateException - 子线程异常退出未调用
countDown(),导致await()永久阻塞
使用场景明确包括:
- 服务启动时等待多个初始化任务(DB 连接、配置加载、缓存预热)全部完成
- 测试中让主线程等所有并发请求线程执行完毕再断言结果
- 批处理中汇总多个子任务结果后统一落库
CyclicBarrier 适合“大家到齐就一起干,干完再一起干下一轮”
它本质是集合点:构造时指定参与线程数(比如 new CyclicBarrier(4)),每个线程到达就调用 await(),当第 4 个线程调用时,所有 4 个线程同时被唤醒继续执行;之后屏障自动重置,可立即用于下一轮。
关键细节:
-
await()既是等待动作,也是“到站打卡”动作 —— 每次调用都使内部计数器 +1 - 可选传入
Runnable(如new CyclicBarrier(4, () -> log.info("All arrived"))),在最后一人到达、所有线程释放前执行一次 - 任意线程在
await()中被中断,整个屏障会进入broke状态,后续所有await()都抛BrokenBarrierException
典型适用场景:
- 并行矩阵计算:每轮迭代中,各线程算完局部结果,必须全部到达屏障后才开始下一轮
- 压力测试工具:N 个线程同步启动,执行请求,再同步收集指标,重复多轮
- 分布式协调模拟:多个模拟节点需在每个阶段末尾确认彼此状态一致
别被“都用 await()”误导,看谁在控制节奏
CountDownLatch 是被动等待者:调用 await() 的线程不参与计数递减,只消费结果;countDown() 才是推进者,通常由工作线程调用。
CyclicBarrier 是主动汇合者:每个线程自己调用 await() 就是在推进计数,没有单独的“推进接口”。所有参与者地位对等。
性能与兼容性影响:
- 高并发下
CyclicBarrier的await()开销略高于CountDownLatch.await(),因需原子更新计数 + 条件唤醒 + 可选回调执行 - 两者都基于 AQS,JDK 8+ 表现稳定;但
CyclicBarrier的reset()方法会中断所有等待线程,若没妥善捕获InterruptedException或BrokenBarrierException,容易引发连锁异常 - 如果只是单次等待,硬用
CyclicBarrier属于杀鸡用牛刀;若需多次复用却选了CountDownLatch,就得手动 new 新实例,增加 GC 压力和对象管理成本
真正容易被忽略的是异常传播路径:CountDownLatch.await() 只抛 InterruptedException;而 CyclicBarrier.await() 会抛三种:被中断时的 InterruptedException、屏障损坏时的 BrokenBarrierException、以及回调 Runnable 抛出的未捕获异常 —— 后者会包装成 RuntimeException 直接从 await() 冒出,不加 try-catch 就崩。










