通过前面对CyclicBarrier(请参考JUC-- CyclicBarrier学习(一)简介和使用)的介绍,我们对CyclicBarrier的使用有了一个深度的认识,现在就要针对CyclicBarrier的源码进行分析,CyclicBarrier的结构如下:
从上图我们可以猜想CyclicBarrier的实现主要就是依靠ReentrantLock和Condition。
首先我们这里来看一下CyclicBarrier属性的意义。
//进入Barrier的锁
private final ReentrantLock lock = new ReentrantLock();
//等待的条件,直到到达屏障点
private final Condition trip = lock.newCondition();
//需要到达屏障点的线程数量
private final int parties;
//所有线程都到达屏障点后需要执行的动作
private final Runnable barrierCommand;
//当前代
private Generation generation = new Generation();
//剩余需要到达屏障点的线程数目
private int count;
从上面的代码我们发现了一个很重要的类Genertion,这个类的源码如下:
private static class Generation {
boolean broken = false;
}
这个类的主要作用就是表示一次CyclicBarrier的使用,当broken属性位true的时候就表示本次使用结束。
CyclicBarrier有两个构造函数,但是最终其实所有的初始化都是通过一个构造函数来完成的,所以我们这里直接来看看这个构造函数做了哪些事。
public CyclicBarrier(int parties, Runnable barrierAction) {
//当使用循环屏障的线程数小于0的时候直接抛出异常
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
上面的构造函数做了三件事:
(1)设置需要到达屏障点的线程数量。
(2)设置剩余需要到达屏障点的线程数量,这里和parties相等。
(3)设置所有需要到达屏障点的线程到达屏障点后需要执行的动作。
我们可以发现在使用CyclicBarrier的时候,主要使用的就是await函数,所以这里我们仅仅对await函数进行分析。其余的可以自行查看源码了解。
这里我们先来看一下await函数的调用顺序。
我们直到调用了await方法后,在所有参与了barrier的方法调用await之前将一直等待。接下来我们来看一看await的源码。
public int await() throws InterruptedException, BrokenBarrierException {
try {
//直接返回dowait的调用结果
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
await函数内部直接调用的是dowait函数。
//第一个参数表示是否限制等待时间,第二个参数是等待时长
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//获取重入锁,进入屏障加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//分代
final Generation g = generation;
//当前generation“已损坏”,抛出BrokenBarrierException异常
//抛出该异常一般都是某个线程在等待某个处于“断开”状态的CyclicBarrie
if (g.broken)
throw new BrokenBarrierException();
//如果当前线程中断,终止CyclicBarrier
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//进来一个线程 count - 1
int index = --count;
//count == 0 表示所有线程均已到位,触发Runnable任务
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//触发任务
if (command != null)
command.run();
ranAction = true;
//唤醒所有等待线程,并更新generation
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
for (;;) {
try {
//如果不是超时等待,则调用Condition的await进行等待
if (!timed)
trip.await();
//如果是超时等待,则调用Condition的awaitNanos进行等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//当前代发生线程中断的异常则终止CyclicBarrier
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
//generation已经更新,返回index
if (g != generation)
return index;
//“超时等待”,并且时间已到,终止CyclicBarrier,并抛出异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放锁
lock.unlock();
}
}
从上面的源码我们可以看出CyclicBarrier的实现主要是依靠ReentrantLock和Condition。
接下来我们来看看两个比较重要的函数breakBarrier和nextGeneration做了些什么。
/**
* 设置当前屏障代无法使用,并且唤醒所有在屏障点等待的线程,
* 注意这里没有生成新的屏障代,所以CyclicBarrier无法继续使用
*/
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
/**
* 生成新的屏障代,并且唤醒所有在屏障点等待的线程
*/
private void nextGeneration() {
//唤醒所有在屏障点等待的线程
trip.signalAll();
count = parties;
//生成新的屏障代,以保证CyclicBarrier可以多次使用
generation = new Generation();
}
到此,已经对CyclicBarrier有了一个深入的学习,后面会继续学习并总结JUC下面的内容。
谢谢关注,如有不同看法欢迎指正。