CyclicBarrier(同步屏障)是什么?它的原理和用法是什么?
CyclicBarrier
是 Java 并发包(java.util.concurrent
)中的一种同步工具,用于让一组线程互相等待,直到所有线程都到达某个公共屏障点(barrier point)后,再继续执行后续任务。其名称中的 “Cyclic”(循环)表明它可以被重复使用。
核心原理
-
计数器机制:
- 初始化时设置一个参与线程数 N(屏障的阈值)。
- 每个线程调用
await()
时,计数器减 1,并进入等待状态。 - 当第 N 个线程调用
await()
后,计数器归零,所有等待线程被唤醒,继续执行。
-
可选的屏障动作:
- 构造时可传入一个
Runnable
任务(称为屏障动作)。 - 当所有线程到达屏障时,由最后一个到达的线程执行此任务(其他线程仍处于唤醒过程中)。
- 构造时可传入一个
-
循环重置:
- 当线程被释放后,计数器自动重置为初始值 N,可立即用于下一轮同步(区别于一次性工具
CountDownLatch
)。
- 当线程被释放后,计数器自动重置为初始值 N,可立即用于下一轮同步(区别于一次性工具
-
异常处理:
- 若等待中线程被中断或超时,会抛出
BrokenBarrierException
,屏障状态变为“损坏”,需调用reset()
重置。
- 若等待中线程被中断或超时,会抛出
核心方法
方法 | 说明 |
---|---|
CyclicBarrier(int parties) | 创建屏障,指定参与线程数 |
CyclicBarrier(int parties, Runnable barrierAction) | 创建屏障,并指定屏障触发时的回调任务 |
int await() | 等待所有线程到达屏障(可中断) |
int await(long timeout, TimeUnit unit) | 带超时的等待 |
void reset() | 强制重置屏障(等待中的线程会抛出异常) |
int getParties() | 获取参与线程数 |
boolean isBroken() | 检查屏障是否损坏 |
使用场景
- 多阶段任务:将大任务拆分为多个阶段,每个阶段需所有子任务完成后才能进入下一阶段。
- 并行计算:多个线程计算不同部分,最终合并结果。
- 模拟测试:模拟高并发场景(如压力测试中等待所有线程就绪后同时发起请求)。
代码示例
import java.util.concurrent.*;public class CyclicBarrierDemo {public static void main(String[] args) {final int THREAD_COUNT = 3;// 创建屏障,并设置屏障触发时的回调任务CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> System.out.println("【屏障触发】所有线程已就位,执行合并任务!"));for (int i = 0; i < THREAD_COUNT; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " 开始执行子任务...");Thread.sleep((long) (Math.random() * 2000)); // 模拟任务耗时System.out.println(Thread.currentThread().getName() + " 子任务完成,等待其他线程");// 等待其他线程到达屏障barrier.await();System.out.println(Thread.currentThread().getName() + " 继续后续操作");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}, "线程-" + i).start();}}
}
输出可能结果:
线程-0 开始执行子任务...
线程-1 开始执行子任务...
线程-2 开始执行子任务...
线程-0 子任务完成,等待其他线程
线程-2 子任务完成,等待其他线程
线程-1 子任务完成,等待其他线程
【屏障触发】所有线程已就位,执行合并任务!
线程-1 继续后续操作
线程-0 继续后续操作
线程-2 继续后续操作
注意事项
-
异常处理:
- 若线程在
await()
时被中断,会抛出InterruptedException
。 - 若屏障在等待期间被重置或损坏,会抛出
BrokenBarrierException
。
- 若线程在
-
屏障重置:
- 调用
reset()
会强制重置屏障,但正在等待的线程会立即抛出异常。 - 屏障正常触发后会自动重置,无需手动操作。
- 调用
-
性能影响:
- 大量线程频繁等待可能成为性能瓶颈,需合理设计线程数量。
与 CountDownLatch 的区别
特性 | CyclicBarrier | CountDownLatch |
---|---|---|
重用性 | ✅ 可循环使用 | ❌ 一次性 |
计数器 | 自动重置 | 手动重置(需重新创建实例) |
核心动作 | 线程互相等待 | 线程等待外部事件(计数器归零) |
任务触发 | 支持屏障动作(回调) | 无内置回调机制 |
适用场景 | 多阶段并行任务 | 启动准备、结束通知等一次性场景 |
简单类比:
CountDownLatch
:裁判发令枪(等待所有运动员就位后开枪)。CyclicBarrier
:团队集合点(所有成员到齐后继续下一步行动)。
通过合理使用 CyclicBarrier
,可以高效解决多线程分阶段协作问题,提升程序的并行性和健壮性。
你想要的技术资料我全都有