应选用CyclicBarrier而非CountDownLatch的场景是:多个线程需反复在屏障点同步等待、齐步推进,如多阶段并行计算、分批数据处理或回合制游戏;因其可重用、支持屏障动作回调,而CountDownLatch仅一次性且无回调。

什么时候该用 CyclicBarrier 而不是 CountDownLatch
当多个线程需要反复在某个点“等齐”再一起往下走,比如分批处理数据、模拟多玩家回合制游戏、或并行计算中多个阶段需同步推进时,CyclicBarrier 更合适。它可重用,而 CountDownLatch 一旦计数归零就不可重置;另外 CyclicBarrier 支持在所有线程到达后自动执行一个 Runnable(例如汇总结果),这个回调是 CountDownLatch 没有的。
常见误用:用 CountDownLatch 去等 N 个线程“全部启动完成”,结果发现主线程放行后子线程才刚初始化——这不是等待“到达”,而是等待“就绪”,更适合用 CyclicBarrier 配合显式 await() 控制节奏。
CyclicBarrier 的构造与基本 await() 调用
创建时指定参与线程数,可选传入一个屏障动作(Runnable):
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("3 个线程已齐,开始下一阶段");
});
每个线程执行到关键同步点时调用 await():
立即学习“Java免费学习笔记(深入)”;
- 成功返回表示当前线程是第 N 个到达者,屏障被触发
- 抛出
BrokenBarrierException表示屏障已被中断或重置(比如有线程超时或被中断) - 抛出
InterruptedException表示当前线程在等待时被中断
注意:await() 是阻塞调用,必须确保所有参与线程都确实会执行到它,否则屏障永远无法触发,其余线程将无限等待。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
如何安全处理超时和异常中断
await(long timeout, TimeUnit unit) 是更健壮的选择,尤其在线程可能因外部原因卡住时:
- 超时后抛出
TimeoutException,此时屏障进入 broken 状态,后续所有await()都会立即抛BrokenBarrierException - 若想恢复使用,必须调用
reset()—— 但要注意:这会唤醒所有正在等待的线程,并让它们收到BrokenBarrierException - 不要在屏障动作(
Runnable)里抛异常,否则整个屏障会被标记为 broken,且异常会传播给最后一个到达的线程
典型陷阱:在屏障动作中做 I/O 或远程调用,没加 try-catch 导致屏障意外 broken,后续批次全部失败。
多阶段同步的实际组织方式
把阶段性逻辑封装进一个可复用的结构里,避免手动管理线程生命周期和屏障状态:
for (int round = 0; round < 5; round++) {
// 启动本轮 3 个 worker
for (int i = 0; i < 3; i++) {
new Thread(() -> {
doWork(round);
try {
barrier.await(); // 等本阶段所有人做完
} catch (Exception e) {
Thread.currentThread().interrupt();
return;
}
afterBarrier(round); // 所有人都到了才执行
}).start();
}
}
关键点:每轮都应确保新线程启动,而不是复用旧线程(否则 await() 可能被重复调用导致 IllegalMonitorStateException);如果用线程池,要确认任务不会因拒绝策略丢失,且屏障实例生命周期覆盖全部阶段。
容易被忽略的是:CyclicBarrier 不保证各线程在屏障后的执行顺序,也不控制它们下一轮是否仍参与——这些需由业务逻辑自行协调,屏障只负责“等齐”这一件事。

















