CyclicBarrier用于多线程分阶段协同,等待所有线程到达屏障点后一起继续,可重复使用并支持屏障动作;CountDownLatch用于一次性倒计时,等待固定事件完成后再放行,不可重用。

Java 提供了丰富的并发工具类来协调多线程的执行步调,其中 CyclicBarrier 和 CountDownLatch 是最常用、最直接支持“步调协同”与“倒计时”语义的两个类。它们底层基于 AQS(AbstractQueuedSynchronizer),使用简单但语义明确:一个用于**等待所有线程到达共同屏障点再一起继续**(协同),另一个用于**等待固定数量的事件完成后再放行**(倒计时)。关键在于选对工具、理解触发时机、避免常见陷阱。
用 CyclicBarrier 实现多线程步调协同
CyclicBarrier 适用于多个线程需要**分阶段同步执行**的场景,比如并行计算中每轮迭代都必须等所有线程完成当前任务后,才统一进入下一轮。它可重复使用(“Cyclic”即由此而来),还可指定一个可选的屏障动作(Runnable),在最后抵达的线程触发释放前执行。
基本用法示例:
- 构造时传入参与协同的线程总数,例如
new CyclicBarrier(4)表示等待 4 个线程都调用await() - 每个线程在关键同步点调用
barrier.await()—— 此处会阻塞,直到第 4 个线程也到达 - 一旦达到阈值,所有线程被同时唤醒,继续后续逻辑;屏障自动重置,可再次使用
- 若需在全体就绪时执行一次汇总操作(如打印统计、校验结果),可在构造时传入
Runnable参数
用 CountDownLatch 实现一次性倒计时触发
CountDownLatch 更适合“主等从”或“事件驱动”的倒计时模型:它初始化一个计数器,每次调用 countDown() 减 1,当计数归零时,所有因 await() 阻塞的线程被唤醒。它不可重用,是一次性门闩(latch)。
立即学习“Java免费学习笔记(深入)”;
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
典型适用场景包括:
- 主线程等待多个子任务全部完成后再汇总结果
- 服务启动时等待若干初始化组件就绪
- 模拟“倒计时开始”信号(如用
new CountDownLatch(1),由某线程调用countDown()触发全局启动)
注意:await() 支持超时参数,可防止无限等待;而 countDown() 是无锁、轻量级操作,可在任意线程安全调用。
关键区别与选型建议
二者表面相似,但设计意图和生命周期不同:
-
CyclicBarrier是“线程间相互等待”,强调**对等协作**,所有参与者地位相同,且可循环使用 -
CountDownLatch是“等待事件发生”,强调**条件达成**,常用于“一等多”或“多通知一”,且计数器归零即失效 - 若需多次协同(如多轮训练、周期性批处理),优先选
CyclicBarrier - 若只是等待一组异步操作结束、或发布一个启动/关闭信号,
CountDownLatch更自然简洁
实用注意事项
实际使用中容易忽略以下细节:
- 两种类的
await()方法都会响应中断,抛出InterruptedException,务必正确处理(恢复中断状态或退出逻辑) -
CyclicBarrier在任一线程 await 时被中断或超时,会导致整个屏障被打破(BrokenBarrierException),其他等待线程也会立即抛异常,需统一捕获并决策是否重试 - 避免在屏障动作(
CyclicBarrier构造参数)或 latch 的 await 后代码中执行耗时操作,否则会拖慢整体协同节奏 - 不要用
CountDownLatch替代线程池的shutdown() + awaitTermination()—— 后者更语义清晰且具备线程池管理能力

















