CyclicBarrier 超详细完整讲解
一、基础概述
java.util.concurrent.CyclicBarrier 中文:循环屏障 / 栅栏
核心思想
一组线程互相等待,所有线程都抵达屏障点之后,才统一继续往下执行; 屏障可以重复使用(Cyclic = 循环)。
形象理解: 一群人相约在门口集合,必须所有人到齐,才能一起出发; 出发完成后,屏障重置,可以进行下一轮集合。
和 CountDownLatch 最直观区别:
- CountDownLatch:主线程等工人,门只能开一次
- CyclicBarrier:工人互相等工人,门开完可以重置,多轮复用
二、构造方法
// n:参与等待的线程总数 public CyclicBarrier(int parties) // parties:等待线程数量;barrierAction:所有人到达屏障后,优先执行的任务(由最后到达的线程执行) public CyclicBarrier(int parties, Runnable barrierAction)
三、核心 API
1. int await() throws InterruptedException, BrokenBarrierException
线程到达屏障,阻塞等待其他线程。 返回值:当前线程抵达次序(0 ~ parties-1,最后到达的线程返回 0)
抛出异常:
InterruptedException:等待过程线程被中断BrokenBarrierException:屏障破损(有线程中断、超时,屏障作废)
2. int await(long timeout, TimeUnit unit)
带超时等待: 超时仍没凑齐所有线程 → 抛出 TimeoutException,屏障会被标记为破损。
3. boolean isBroken()
判断屏障是否破损。 触发破损场景:
- 某个等待线程 await () 时被中断
- await 超时 一旦破损,其他正在等待的线程立刻收到
BrokenBarrierException
4. void reset()
手动重置屏障,开启新一轮等待。
如果此时还有线程正在等待,这些线程会抛出 BrokenBarrierException。
5. int getParties()
获取需要凑齐的总线程数
6. int getNumberWaiting()
当前正在屏障处阻塞等待的线程数量(瞬时快照)
四、简单示例(基础用法)
import java.util.concurrent.CyclicBarrier;
public class BarrierDemo {
public static void main(String[] args) {
// 等待3个线程全部到达屏障
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 【屏障任务】所有线程到齐后,由最后到达的线程执行
System.out.println("==== 所有人到齐,统一出发 ====");
});
for (int i = 1; i <= 3; i++) {
int num = i;
new Thread(() -> {
try {
System.out.println("线程" + num + "到达屏障,等待其他人");
// 阻塞,等凑齐3个线程
barrier.await();
System.out.println("线程" + num + "继续执行后续任务");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
执行顺序特点: 三个线程全部打印「到达屏障」之后,才统一打印「继续执行」。
五、循环复用特性(关键区别 CountDownLatch)
CyclicBarrier barrier = new CyclicBarrier(2, () -> System.out.println("一轮完成"));
// 第一轮
new Thread(() -> {try{barrier.await();}catch(Exception e){}}).start();
new Thread(() -> {try{barrier.await();}catch(Exception e){}}).start();
// 短暂等待后,第二轮可以继续使用同一个barrier
Thread.sleep(1000);
new Thread(() -> {try{barrier.await();}catch(Exception e){}}).start();
new Thread(() -> {try{barrier.await();}catch(Exception e){}}).start();
CountDownLatch 计数器归零直接报废,无法第二轮等待。
六、底层原理简述
CyclicBarrier 没有直接使用 AQS,底层依靠:
ReentrantLock+Condition内部维护变量:
parties:总等待线程数(固定不变)count:当前还未抵达屏障的线程数Generation(代) 每一轮成功放行后,新建一个 Generation,代表新一轮屏障; 如果出现中断 / 超时 → 当前 Generation 标记为 broken(破损)。
流程:
- 线程调用 await () → lock 加锁
- count 自减
- count > 0:condition.await () 阻塞
- count == 0:执行 barrierAction,唤醒所有等待线程,重置 count,新建 Generation,开启下一轮
七、典型业务场景
- 多阶段并行计算 多线程分块计算数据;第一阶段全部算完,统一汇总,再进入第二阶段计算。 例如:大数据分片处理 → 分片计算完成再合并,合并完再启动下一轮分片。
- 模拟多玩家同步游戏 所有玩家加载资源完毕后,才同时进入游戏场景。
- 批量任务分阶段执行 一批任务分成多个阶段,上一阶段所有线程完成,才能开启下一阶段。
八、CyclicBarrier vs CountDownLatch 深度对比
| 对比项 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 核心角色 | 外部线程等待一组工作线程 | 一组线程之间互相等待 |
| 复用性 | 一次性,计数器归零失效 | 支持循环重复使用 |
| 计数器方向 | 只能递减,不可重置 | 每一轮凑齐后自动重置 count |
| 回调任务 | 无内置回调 | 支持屏障回调(最后到达线程执行) |
| 异常机制 | 不会 “连锁异常”,某线程异常不影响其他等待线程 | 某个线程中断 / 超时 → 屏障破损,其他等待线程抛出 BrokenBarrierException |
| 底层 | AQS 共享锁 | ReentrantLock + Condition + Generation |
| await 含义 | 等待计数器到 0 | 等待所有同伴抵达屏障 |
一句话区分:
- CountDownLatch:老板等员工干完活
- CyclicBarrier:员工互相等待,所有人到齐再开工,干完可以再来一轮
九、高频坑点(生产避坑)
坑 1:线程数量不等于 parties
示例:barrier = new CyclicBarrier (3),但只启动 2 个线程调用 await () → 永久阻塞,程序卡死。
坑 2:屏障破损后不处理异常
只要其中一个线程 await 超时 / 被中断,屏障 broken = true; 其他正在 await 的线程立刻抛出 BrokenBarrierException。 解决方案:捕获异常后,可以手动 reset() 尝试重建屏障。
坑 3:混淆 barrierAction 执行线程
barrierAction 由最后调用 await () 的那条线程执行,不是主线程! 不要在里面放耗时阻塞任务,会拖慢整体放行。
坑 4:忘记 await 超时机制
生产环境一律推荐使用带超时 await(time, unit),防止死锁。
十、扩展:经典多阶段计算示例
public class MultiStageDemo {
public static void main(String[] args) throws InterruptedException {
int threadNum = 3;
CyclicBarrier barrier = new CyclicBarrier(threadNum, () -> {
System.out.println("==== 当前阶段全部执行完毕,进入下一阶段 ====");
});
for (int i = 1; i <= threadNum; i++) {
int idx = i;
new Thread(() -> {
try {
// 阶段1
System.out.println("线程" + idx + ":执行阶段1");
Thread.sleep(500);
barrier.await();
// 阶段2
System.out.println("线程" + idx + ":执行阶段2");
Thread.sleep(500);
barrier.await();
// 阶段3
System.out.println("线程" + idx + ":执行阶段3");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
运行现象:严格分阶段推进,必须全部线程完成当前阶段,才能进入下一阶段。
十一、三者速记(CountDownLatch / CyclicBarrier / Semaphore)
- CountDownLatch:一次性等待,外部线程等待一批任务完成
- CyclicBarrier:循环栅栏,线程互相等待,多阶段计算首选
- Semaphore:信号量,控制并发数量(限流、资源池)

浙公网安备 33010602011771号