CyclicBarrier支持所有线程互相等待直到所有线程都到达一个屏障点。
CyclicBarrier的入参是1个数字,标识CyclicBarrier屏障关闭需要被几个线程调用await方法。还有1个可选参数,是Runnable类型,标识屏障被打开时被执行的行为。
CyclicBarrier(int parties, Runnable barrierAction)CyclicBarrier(int parties)CyclicBarrier最重要的方法是await方法。
我们先进到await方法,发现调用了dowait方法,关键的逻辑也在dowait里面。
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}为了方便描述,我们默认屏障默认是打开的,所有线程调用await方法后屏障是关闭的。
我们把CyclicBarrier的dowait方法分为4部分
- 前置校验
- 屏障关闭处理逻辑
- 屏障没有关闭处理逻辑
- 后置处理
前置校验
首先,给dowait方法加锁。
然后获取1个Generation对象,CyclicBarrier是个循环屏障,每次屏障被打开,或者被reset,会生成1个新的Generation对象,标识当前屏障的时代。Generation只有1个变量broken,表示当前时代的屏障是否被打破。获取到当前时代之后,判断是否被打破了,如果被打破了,就抛1个异常。
然后判断当前线程是否被中断,如果被中断,就打破当前屏障。
final ReentrantLock lock = this.lock;
lock.lock();
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}打开breakBarrier方法,发现把时代里的是否打破屏障的标识改为true,然后把等待打开屏障条件的线程全部唤醒。
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}屏障关闭处理逻辑
count变量表示离打开屏障还需要被调用多少次。
这里减1之后,如果判断等于0,则表示屏障可以打开。
这里先获取需要被回调的Runnable实现,如果不是空,就执行实现。
执行之后把时代更新为下1个时代。
在finally代码块里,判断如果ranAction是false,就打破屏障。这里是为了防止Runnable实现有问题导致所有线程一直阻塞,同时认为这个屏障不应该再被使用。
int index = --count;
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();
}
}我们看下是如果更新时代的。
现唤醒所有等待打开屏障条件的线程,然后重制count的值变为parties(构造函数入参),然后创建下个时代对象。
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}屏障没有关闭处理逻辑
将线程在等待屏障关闭事件等待,这里有不带限时时间和带限时事件2种实现方式。
如果线程被中断,就判断时代是否改变,如果没有改变而且屏障没有打破,那就打破屏障抛出异常。否则就将线程再次中断。
如果屏障被打破,就抛出异常。
如果时代更改,正常返回。
如果超时,打破屏障,抛出异常。
// loop until tripped, broken, interrupted, or timed outfor (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}这些逻辑都处理完之后,进行解锁。
总结
- await操作基本上都在同步执行
- 回调实现有问题、线程中断、超时都会打破屏障
- 屏障关闭、reset会更新时代