* A synchronization aid that allows a set of threads to all wait for
* each other to reach a common barrier point. CyclicBarriers are
* useful in programs involving a fixed sized party of threads that
* must occasionally wait for each other. The barrier is called
* cyclic because it can be re-used after the waiting threads
* are released.
其作用在于使一组线程相互等待直到到达某个共同点(类似于栅栏)。循环的意思为这个栅栏可以被重复使用(即第一次等待所有线程到达共同点后,会重新初始化),后文会介绍。
在满足什么情况下等待(等待条件),如何等待(已知有Object.wait(),Condition.await()方法)。
在满足什么情况下唤醒(唤醒条件),如何唤醒(已知有Object.notifyAll(),Codition.signalAll()方法)。
在满足什么情况下重新初始化栅栏(初始化条件),如何初始化(源码采用Generation实现,请看后文)。
假设N个线程相互等待,初始化count=N,每个线程判断如果N!=0则进行等待并将N=N-1,如果某个线程判断N=0,则唤醒所有等待的线程,并重新更新Barrier使之可以重复使用。
/** The lock for guarding barrier entry */
//作用一:产生Condition对象 作用二:同步await()方法。
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
//问题一问题二中等待唤醒采用Condition.await()/awaitNanos(long)和Condition.signalAll()方法。
private final Condition trip = lock.newCondition();
/** The number of parties */
//循换栅栏可以重复使用,那么当再次进行初始化的时候,根据这个值进行初始化等待线程的个数(只是构造函数传参的一个副本,不随着await()方法的调用而递减,相反的是count属性)。
private final int parties;
/* The command to run when tripped */
//栅栏提供的额外功能,即当所有线程达到共同点后,由最后一个(count=0)达到的线程执行这个命令(先执行这个命令然后再唤醒其他线程),可以想一下使用场景。
private final Runnable barrierCommand;
/** The current generation */
//实现循环栅栏,表示当前栅栏所属年代,当所有线程到达共同点后会用nextGeneration对栅栏进行重新初始化并更新年代。
private Generation generation = new Generation();
/**
* Number of parties still waiting. Counts down from parties to 0
* on each generation. It is reset to parties on each new
* generation or when broken.
*/
//表示还有多少线程在进行等待,每当一个线程调用await()方法之后,会减一,当为0时唤醒其他等待线程。
private int count;
public CyclicBarrier(int parties) {
this(parties, null);
}
//初始化以上解释的几个参数。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
//进行线程的等待,唤醒操作,返回剩余等待的线程数。
public int await() throws InterruptedException, BrokenBarrierException {
try {
//主要调用dowait方法
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
//timied:是否设置了等待超时 nanos:如果设置了超时,超时时间,纳秒为单位
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//这里同步的目的有如下两个原因
//一:调用Condition.await()方法之前,该线程必须持有锁,否则抛出IllegalMonitorStateException。
//二:需要对count进行减一操作,多线程情况下要么用CAS,要么同步,在原因一的基础上这里用同步一并解决。
lock.lock();
try {
//保存当前栅栏的年代,下文会用到
final Generation g = generation;
//当前栅栏处于broken状态,可以通过源码查看如果存在以下情况,则设置Generation .broken=true
//一:当某一个线程被中断;栅栏中如果某一线程被中断,那么其他等待的线程则会全部抛出BrokenBarrierException。
//二:执行传入的Runnable任务时抛出异常。
//三:调用Barrier.reset()方法。
//四:某个线程调用了await(long,TimeUnit)方法,且达到超时时间。
if (g.broken)
throw new BrokenBarrierException();
//当前线程被中断,则销毁当前栅栏
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//线程调用await()方法只有,对应的count=count-1,因为用了同步,所以线程安全。
int index = --count;
//问题二的共同点
if (index == 0) { // tripped
boolean ranAction = false;
try {
//所有线程到达共同点时,由最后一个线程(count=0)执行命令
final Runnable command = barrierCommand;
if (command != null)
command.run();
//执行完命令设为true,如果执行命令中抛出异常,那么依然为false。
ranAction = true;
//当执行完命令之后,唤醒所有等待线程,更新Barrier的generation,重新初始化barrier。
nextGeneration();
return 0;
} finally {
//当执行command命令抛出异常时,销毁当前barrier。
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//经历以上步骤,如果还没有到达共同点,则让当前线程在Condition上等待。
if (!timed)
trip.await();
else if (nanos > 0L)
//返回的nanos代表{@code nanosTimeout}值的估计值减去从该方法返回时等待的时间。一个正值可以用作对该方法的后续调用
//的参数,以完成所 需的等待时间。小于或等于零的值表示时间所剩无几。
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//线程被中断,且还没有达到共同点(因为达到共同点之后g!=generation),如果barrier没有被销毁,则进行销毁。
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;
//如果设置了等待,且等待时间到达(看上面awaitNanos解释),则销毁栅栏。否则继续for循环。
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
//销毁栅栏,之后,所有等待的线程都会抛出BarrierBrokenExcepition
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
//当到达共同点后,唤醒所有等待线程并重新初始化栅栏,产生新的generation。
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
以上就是CyclicBarrier源码的分析,其主要是借用了ReentrantLock和Condition实现线程的协作(等待和唤醒),并通过count控制栅栏的共同点。通过到达共同点后,刷新栅栏来达到重用的目的。
-------------------------------------------------------------------------------------------------------------------
知其然,更要知其所以然...
-------------------------------------------------------------------------------------------------------------------