CyclicBarrier作用:
CyclicBarrier用于同步一组线程,创建CyclicBarrier时指定参与循环栅栏的线程数量与一组线程全部就位时,优先执行的动作。 每个线程调用await()方法后,即表示到达栅栏点,然后就会进阻塞,当参与循环栅栏的最后一个线程调用了await()方法后,会唤醒之前阻塞的线程,循环栅栏重置,进入下一次循环。
CyclicBarrier字段解释:
// 表示本次循环的状态:为true时,表示本次循环被破坏(如,有线程被中断、await超时)
private static class Generation {
boolean broken = false;
}
/** 在执行await时是独占的 */
private final ReentrantLock lock = new ReentrantLock();
/** 用于多个线程之间的同步 */
private final Condition trip = lock.newCondition();
/** 参与一次循环的线程数 */
private final int parties;
/* 所有都达到循环栅栏点时,优先执行的动作 */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
/** 用来表示当前剩余的需要到达栅栏点的线程数量 **/
private int count;
核心逻辑实现:
CyclicBarrier核心逻辑的实现: dowait(boolean timed, long nanos)。
当调用await方法后,最后会调用 dowait(boolean timed, long nanos)。
执行流程:
首次明确这个过程需要加锁,因为会涉及到多个共享变量的赋值与线程之间的同步。
拿到锁后,先检查本次循环的状态是否被破坏,若被破坏则直接抛出异常。
然后检查当前线程是否被中断,若被中断调用breakBarrier()去终止本次循环:将循环状态置为无效状态,将剩余需要到达栅栏点的线程数置为初始值,最后唤醒所以调用await进入阻塞状态的线程。breakBarrier()执行完毕后抛出中断异常。
检查通过的话,将需要进入栅栏点的线程数减一(–count)
减完后的值若为0:代表本次循环执行完毕,此时若创建CyclicBarrier时指定了到达栅栏点需要执行的任务,则先执行该任务,然后调用nextGeneration()去更新状态,为下一次循环初始化: 先唤醒所有等待的线程,将count设置为初始值parties,新建一个Generation赋值给generation变量。
如果–count后的值不为0,则代表还有线程未准备就绪,则进入阻塞过程,阻塞过程发生中断调用breakBarrier()。当从阻塞状态被唤醒后,会检查当前代(generation)是否被破坏,破坏直接抛出栅栏被破坏。未被破坏,检查是否正常换代,即检查进入await方法时拿到的 generation的引用(g)与现在的generation还是否是同一个?在–count为0时,执行了nextGeneration()方法,该方法会新建一个Generation赋值给generation变量。所以在这里如果g!=generation代表成功换代。
参照源码理解上述过程:
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()) {
// 将损坏状态设置为true
// 并通知其他阻塞在此栅栏上的线程
breakBarrier();
throw new InterruptedException();
}
// 获取下标
int index = --count;
// 如果是 0,说明最后一个线程调用了该方法
if (index == 0) {
// tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
// 执行栅栏任务
if (command != null)
command.run();
ranAction = true;
// 更新一代,将count重置,将generation重置
// 唤醒之前等待的线程
nextGeneration();
return 0;
} finally {
// 如果执行栅栏任务的时候失败了,就将损坏状态设置为true
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
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();
}
}
// 当有任何一个线程中断了,就会调用breakBarrier方法
// 就会唤醒其他的线程,其他线程醒来后,也要抛出异常
if (g.broken)
throw new BrokenBarrierException();
// g != generation表示正常换代了,返回当前线程所在栅栏的下标
// 如果 g == generation,说明还没有换代,那为什么会醒了?
// 因为一个线程可以使用多个栅栏,当别的栅栏唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。
// 正是因为这个原因,才需要generation来保证正确。
if (g != generation)
return index;
// 如果有时间限制,且时间小于等于0,销毁栅栏并抛出异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
// 释放独占锁
lock.unlock();
}
}
让当前栅栏失效:
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
进行换代:
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}