CyclicBarrier 类的作用是什么?请解释其工作原理和适用场景。
参考回答
CyclicBarrier
是 Java 并发包(java.util.concurrent
)中的一个同步工具类,用于让一组线程彼此等待,直到所有线程都到达某个共同的屏障点(barrier)后再继续执行。
- 核心作用
:
- 使多个线程在执行到某一阶段时相互等待。
- 在所有线程都到达屏障点时,可以执行一个额外的任务(可选的
barrierAction
)。 CyclicBarrier
可重复使用,适合场景中需要多次同步的情况。
详细讲解与拓展
1. 工作原理
- 初始参数:
CyclicBarrier
在初始化时,需要指定参与的线程数量(parties
)和一个可选的屏障动作(Runnable
)。- 屏障动作在所有线程到达屏障点后自动执行一次。
- 线程等待机制:
- 线程调用
await()
方法进入等待状态。 - 当指定数量的线程都调用了
await()
后,所有线程被唤醒并继续执行。
- 线程调用
- 循环复用:
CyclicBarrier
名为“Cyclic(循环)”,是因为屏障可以重置后重新使用。
2. 构造方法
CyclicBarrier
提供了两个构造方法:
- 只指定参与线程数:
public CyclicBarrier(int parties)
- 当指定数量的线程都调用
await()
时,屏障点打开,所有线程继续执行。
- 指定线程数和屏障动作:
public CyclicBarrier(int parties, Runnable barrierAction)
barrierAction
是一个可选任务,在所有线程到达屏障点后执行。
3. 方法解析
await()
:- 线程调用此方法进入等待状态,直到所有线程到达屏障点。
- 如果当前线程在等待过程中被中断,会抛出
InterruptedException
。
getNumberWaiting()
:- 返回当前已到达屏障点的线程数量。
isBroken()
:- 检查屏障是否被破坏(例如线程等待时被中断)。
reset()
:- 重置屏障点,将其恢复到初始状态。
4. 示例代码
场景:模拟多个运动员在起跑线准备,所有人都准备好后一起起跑。
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int threadCount = 5;
// 创建 CyclicBarrier,并指定屏障点的线程数
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("All athletes are ready. Start running!");
});
for (int i = 0; i < threadCount; i++) {
new Thread(new Athlete(barrier, i)).start();
}
}
}
class Athlete implements Runnable {
private final CyclicBarrier barrier;
private final int id;
public Athlete(CyclicBarrier barrier, int id) {
this.barrier = barrier;
this.id = id;
}
@Override
public void run() {
try {
System.out.println("Athlete " + id + " is getting ready.");
Thread.sleep((long) (Math.random() * 3000)); // 模拟准备时间
System.out.println("Athlete " + id + " is ready.");
barrier.await(); // 等待其他线程
System.out.println("Athlete " + id + " starts running!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出示例:
Athlete 0 is getting ready.
Athlete 1 is getting ready.
Athlete 2 is getting ready.
Athlete 3 is getting ready.
Athlete 4 is getting ready.
Athlete 2 is ready.
Athlete 1 is ready.
Athlete 0 is ready.
Athlete 3 is ready.
Athlete 4 is ready.
All athletes are ready. Start running!
Athlete 2 starts running!
Athlete 1 starts running!
Athlete 0 starts running!
Athlete 3 starts running!
Athlete 4 starts running!
解释:
- 所有运动员(线程)调用
await()
,进入等待状态。 - 当所有线程都准备好时,触发
barrierAction
输出 “All athletes are ready. Start running!”。 - 线程被唤醒,开始起跑。
5. 常见问题和注意事项
- 屏障被破坏(BrokenBarrierException):
- 当某个线程在
await()
时被中断或超时,屏障会被标记为破坏状态,其他线程调用await()
会抛出BrokenBarrierException
。 - 可以通过
reset()
方法重置屏障。
- 线程超时:
- await()提供超时版本:
“`java
public int await(long timeout, TimeUnit unit) throws TimeoutException
“` -
如果线程等待超时,会抛出
TimeoutException
,同时破坏屏障。
- 线程不满足数量:
- 如果参与线程数不足,线程将一直等待,可能导致死锁。
- 确保指定的线程数与实际线程数一致。
- 屏障动作的设计:
barrierAction
任务执行时间较长可能影响后续线程的进度。- 避免在
barrierAction
中执行阻塞操作。
6. 适用场景
- 分布式任务同步:
- 各个线程独立执行子任务,等待所有子任务完成后继续下一步。
- 示例:多个计算节点协同处理大数据任务。
- 阶段性任务:
- 多线程任务分阶段执行,每阶段完成后同步。
- 示例:流水线任务中,各阶段处理的线程需要同步。
- 模拟场景:
- 模拟多线程同时开始的场景,例如运动员同时起跑、系统服务同时启动。
总结
CyclicBarrier
的作用:- 让一组线程相互等待,直到所有线程到达屏障点后再继续执行。
- 支持循环复用,适合多次同步场景。
- 工作原理:
- 每个线程调用
await()
进入等待状态,计数器减 1。 - 当计数器为 0 时,触发屏障动作并唤醒所有线程。
- 每个线程调用
- 适用场景:
- 用于分布式任务同步、阶段性任务同步、模拟并发等场景。