1. 作用和使用场景
CyclicBarrier主要是实现了一个可循环利用的屏障(同步屏障),即参与的所有线程都执行到某个点时,才能执行后续的代码,只要有一个线程还未执行到指定点位,则其他所有线程都需要进入等待;类似于公司组织会议,要求所有参会人必须到场,只要有一个人没到场,则会议无法开始,已到场的所有人都必须等待。
2. 主要方法
/**
* 构造方法
*
* @param parties 参与线程数
* @param barrierAction 最后一个到达屏障的线程需要执行的动作
**/
public CyclicBarrier(int parties, Runnable barrierAction)
public CyclicBarrier(int parties)
/**
* 带超时时间的等待屏障方法
* @param timeout 超时时间
* @param unit 时间单位
**/
public int await(long timeout, TimeUnit unit)
/**
* 无超时时间的等待屏障方法
**/
public int await()
/**
* 执行等待
* @param timed 是否存在超时
* @param nanos 超时的纳秒数
**/
private int dowait(boolean timed, long nanos)
/**
* 重置屏障
**/
public void reset()
3. 核心原理
CyclicBarrier原理并不复杂,主要是利用ReentrantLock和Condition实现线程之间的消息通知 主要实现方法是dowait
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 先获取锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
// 检查当前屏障是否被打翻
if (g.broken)
throw new BrokenBarrierException();
// 线程接收到中断信号,打翻栅栏
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 每个参与屏障的线程都将计数器-1
int index = --count;
// 当计数器归零时,表示所有线程都已到达屏障处,由最后一个到达栅栏的线程执行barrierCommand
// 唤醒所有参与的线程,并进入下一个栅栏
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
// 唤醒所有线程,并进入下一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
/**
使用死循环 for (;;) 的原因如下:
主要目的
处理虚假唤醒
Condition.await() 方法可能会出现虚假唤醒(spurious wakeup)
需要循环检查条件是否真正满足,而不是仅依赖一次唤醒
多重条件检查
需要反复检查多种情况:
屏障是否被破坏(g.broken)
是否进入下一代(g != generation)
是否超时(nanos <= 0L)
中断处理后的继续等待
当捕获到 InterruptedException 时:
如果屏障未被破坏且仍处于同一代,则调用 breakBarrier() 并抛出异常
否则设置中断状态并继续等待(通过循环重新进入 await 状态)
**/
for (;;) {
try {
if (!timed)
// condition的await方法会释放lock锁
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}