CyclicBarrier 是 Java 中用于多线程周期性同步的可重用工具类,支持到达-触发-重置流程、屏障动作执行及超时控制,适用于分片计算后周期性聚合等场景。

CyclicBarrier 是 Java 并发包(java.util.concurrent)中用于**多线程周期性同步**的工具类,特别适合“多个子任务并行执行 → 每轮结束后统一等待 → 全部到达后继续下一轮”的场景。它比 CountDownLatch 更灵活,因为可重复使用(“Cyclic”即循环之意),且支持在所有线程到达屏障时执行一个可选的“屏障动作”(Runnable)。
核心机制:理解 barrier 的“到达-触发-重置”流程
每个 CyclicBarrier 初始化时指定参与线程数(parties)。每当一个线程调用 await(),就视为“到达屏障”:
- 若未满员,该线程被阻塞,等待其他线程也调用
await(); - 当第
parties个线程调用await(),屏障被“触发”:所有等待线程同时被唤醒; - 若构造时传入了
Runnable,它会在最后到达的线程释放前、所有线程唤醒前执行(常用于汇总、日志、校验等); - 屏障自动重置,可立即用于下一轮同步。
典型应用:多线程分片计算 + 周期性聚合
假设你有 100 万条数据,需按每批 1 万条分给 10 个线程并行处理(如数值变换、模型预测),每处理完一批(即一个“周期”),需汇总结果、更新状态、或检查是否中止。这时 CyclicBarrier 就是自然选择:
- 创建
CyclicBarrier(10, aggregationTask),10 表示 10 个子任务线程; - 每个线程处理完自己那批数据后,调用
barrier.await(); - 第 10 个线程到达时,
aggregationTask执行(例如合并 10 个局部结果到全局容器); - 所有线程从
await()返回,开始下一批处理(或退出)。
关键编码要点与避坑提醒
实际使用中需注意以下细节:
-
异常处理必须覆盖:
await()可能抛出InterruptedException(线程被中断)或BrokenBarrierException(屏障被破坏,如某线程超时离开或被中断)。务必在catch中判断是否需重试、记录或终止整个流程; -
屏障动作(
Runnable)应在毫秒级完成:它由最后一个到达的线程串行执行,若耗时过长,会拖慢所有线程的下一轮启动; -
避免死锁:确保每个参与线程在每个周期内**恰好调用一次
await()**;漏调或重复调用都会导致部分线程永远阻塞或提前触发; -
超时控制很实用:使用
await(long timeout, TimeUnit unit)防止某个子任务因异常卡住,拖垮整体。超时后抛出TimeoutException,此时屏障进入破损状态,需重建或协调恢复。
一个最小可运行示例
以下代码模拟 3 个线程并行生成随机数,每轮后打印各自结果并汇总平均值:
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
double avg = (sum1 + sum2 + sum3) / 3.0;
System.out.println("【汇聚】本轮平均值:" + avg);
});
// 启动 3 个线程,各自循环执行
for (int i = 0; i {
for (int round = 0; round
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











