我们看下面的这段代码:
CyclicBarrier barrier = new CyclicBarrier(2);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
barrier.await();
System.out.println("等待");
} catch (Exception e) {}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
barrier.await();
System.out.println("未等待");
} catch (InterruptedException | BrokenBarrierException e) {}
}
}).start();
上面这段代码一开始实例化了一个类,传入的资源的数目是2,启动了两个线程,其中一个等待2秒后才调用await 方法,另外一个线程直接调用await方法,运行之后发现2秒后他们几乎同时打印出了自己要打印的东西。接下来我们来看一下他的作用和实现原理:
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
构造方法中保存了我们传入的资源的数量,以及一个回调函数,接下来我们就看一下await方法是怎么实现的:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
方法里面调用了dowait方法,但是现在我们还不知道这两个参数的含义是什么,我们继续跟进去:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//获得锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//cyclicBarrier每次一个轮回就代表一代
final Generation g = generation;
//是否被打断
if (g.broken)
throw new BrokenBarrierException();
//当前线程是否中断
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//获得剩余资源的数目
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();
}
}
//否则进入死循环等待
for (;;) {
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();
}
}
} finally {
lock.unlock();
}
}
在这个方法里面用到了可重入锁来保证同步,首先当前线程会获取资源,进而得到还剩下的资源的数目,如果剩下的资源的数目是0,表示这一代已经结束了,需要进行下一代,对下一代进行一个初始化。如果还有剩余的资源,那么当前线程就要放弃锁的资源进入阻塞状态,等待被唤醒,我们来看一下是如何开始新的一代的:
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
首先会唤醒所有在等待的线程,重新初始化我们的资源,开启新的一代。
如果在运行的时候发生了异常那么他又会做什么操作呢:
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
首先会标记当前代被打断了,重新初始化资源,唤醒所有阻塞的线程。
这个类的实现还是比较简单的,最主要是用到了可重入锁的以及Condition,这节没有对Condition进行分析,下一讲我们就来看一下他是怎么实现的。