1.特点分析
- CyclicBarrier是一种同步机制,它可以使得一组线程在同一个障碍点进行等待。
- CyclicBarriers 可以通过重置计数器从而重新使用。
- CyclicBarrier支持一个可选的Runnable命令(实例化构造函数中的参数),该命令在最后一个线程到达后,但在任何线程被释放之前被执行。这一命令在barrier处只会被执行一次,且由最后到达的线程完成。这种屏障行为对于在任何一方继续之前更新共享状态都很有用。
- all-or-none破损模型:如果一个线程因为中断(or执行过程的失败,超时等)过早的离开了barrier点,那么等待在barrier点的其他所有线程也会在同一时间因为BrokenBarrierException或者InterruptedException异常而离开barrier。
- 内存一致性影响:
- 线程在调用await()方法之前的行为要优先于barrier action中的任何行为。
- barrier action成功返回这一行为要优先于所有其他等待线程await()后的行为。
- 使用可重入锁ReentrantLock。
2.部分域和方法的介绍
- private static class Generation 类
- 只有一个域boolean broken=false;
- 用于标识:当前barrier是否处于broken状态。
- private final ReentrantLock lock = new ReentrantLock();
barrier入口的保护锁
- private final Condition trip = lock.newCondition();
trip前的等待条件
- private final int parties;
同时使用这个barrier的线程个数
- private final Runnable barrierCommand;
当trip时,barrier定义的屏障操作.
- private Generation generation = new Generation();
当前generation
- private int count;
- 等待线程数。每一个generation上,count值都会从parties降为0。
- 针对每一个新的generation或者当前barrier被broken时,count值都会被reset。
- 2个构造方法
- public CyclicBarrier(int parties)
- public CyclicBarrier(int parties, Runnable barrierAction)
- private int dowait(boolean timed, long nanos)方法
- 此方法是barrier的主要代码,里面包括类很多实现上的策略.
- await()方法直接调用本方法。
- 执行过程:
- 要加锁的方法
dowait(),isBroken(),reset(),getNumberWaiting()。
3.源码分析
package sourcecode.analysis;
/**
* @Author: cxh
* @CreateTime: 18/4/15 11:11
* @ProjectName: JavaBaseTest
*/
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* CyclicBarrier是一种同步机制,它可以使得一组线程在同一个障碍点进行等待.在涉及到固定个数的线程组且有时必须互相等待时,
* 则CyclicBarriers此时就变得很有用.CyclicBarrier前缀有cyclic,是因为在线程释放后,CyclicBarriers可以通过重置
* 计数器从而重新使用.
*
* CyclicBarrier支持一个可选的Runnable命令,该命令在最后一个线程到达后,但在任何线程被释放之前,这一命令在barrier只会被执行一次,且由最后
* 到达的线程完成。这种屏障行为对于在任何一方继续之前更新共享状态都很有用。
* 使用样例: 在并行分解设计中如何使用一个barrier,请看如下样例:
*
* class Solver {
* final int N;
* final float[][] data;
* final CyclicBarrier barrier;
*
* class Worker implements Runnable {
* int myRow;
* Worker(int row) { myRow = row; }
* public void run() {
* while (!done()) {
* processRow(myRow);
*
* try {
* barrier.await();
* } catch (InterruptedException ex) {
* return;
* } catch (BrokenBarrierException ex) {
* return;
* }
* }
* }
* }
*
* public Solver(float[][] matrix) {
* data = matrix;
* N = matrix.length;
* Runnable barrierAction =
* new Runnable() { public void run() { mergeRows(...); }};
* barrier = new CyclicBarrier(N, barrierAction);
*
* List<Thread> threads = new ArrayList<Thread>(N);
* for (int i = 0; i < N; i++) {
* Thread thread = new Thread(new Worker(i));
* threads.add(thread);
* thread.start();
* }
*
* // wait until done
* for (Thread thread : threads)
* thread.join();
* }
* }
*
* 此例中,每一个worker线程会处理矩阵中的一行数据,然后在barrier处等待直到所有的行被处理完.当所有的行都被处理完成后,
* 然后barrier行为被执行,实现合并行.如果合并时发现已经有线程完成了合并操作则每一个worker线程被终止,且直接返回true.
*
* 如果barrier行为在执行时,并不依赖于挂起的线程组,则线程组中的任意线程在被释放后,都可以去执行barrier行为.
* 为了使得这一功能变得容易实现,await()方法的每一次调用都会返回barrier处到达线程的索引.
* 然后你就可以选择用哪一个线程来执行barrier行为,举例如下:
* if (barrier.await() == 0) {
* // log the completion of this iteration
* }
*
* 在同步失败时,CyclicBarrier使用了一种all-or-none破损模型:如果一个线程因为中断(or执行过程的失败,超时等)过早的离开了barrier点,
* 那么等待在barrier点的其他所有线程也会在同一时间因为BrokenBarrierException或者InterruptedException异常而离开barrier.
*
* 内存一致性影响:线程在调用await()方法之前的行为要优先于barrier action中的任何行为,barrier action成功返回这一行为要优先于
* 所有其他等待线程await()后的行为.
*
*
* @since 1.5
* @see java.util.concurrent.CountDownLatch
*
* @author Doug Lea
*/
public class CyclicBarrier {
/**
* barrier每一次使用都代表了一个generation实例.
* 当barrier发生trip或者reset时,对应的generation会发生改变.
* 由于非确定性,锁可能会分配给等待线程,因此可能会存在许多和使用barrier的线程相关的generation.
* 但是每次只能激活这些线程中的一个(使用计数的那个),并且其他的线程要么broken要么trip.
* 如果出现了一个暂停,但并未reset,则不需要一个激活的generation.
*/
private static class Generation {
boolean broken = false;
}
//barrier入口的保护锁
private final ReentrantLock lock = new ReentrantLock();
//trip前的等待条件
private final Condition trip = lock.newCondition();
//同时使用这个barrier的线程个数
private final int parties;
//当trip时,barrier定义的屏障操作.
private final Runnable barrierCommand;
//当前generation
private Generation generation = new Generation();
//等待线程数.每一个generation上,count值都会从parties降为0.
//针对每一个新的generation或者当前barrier被broken时,count值都会被reset.
private int count;
//当barrier发生trip时,用于更新状态并唤醒每一个线程.
//这一方法只在持有lock时被调用.
private void nextGeneration() {
// signal completion of last generation
//标志最后一个generation完成.
trip.signalAll();
// set up next generation
//为下一个generation赋初值
count = parties;
generation = new Generation();
}
//为当前barrier的generation类的唯一变量broken赋值,并唤醒所有线程.
//这一方法只在持有lock时被调用.
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();//唤醒所有线程.
}
//此方法是barrier的主要代码,里面包括类很多实现上的策略.
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;//定义了一个final类型的可重入锁
lock.lock();//加锁
try {
final Generation g = generation;//获取generation类
//如果当前barrier已经broken了(其它线程出现问题导致barrier发生了broken)
if (g.broken)
throw new BrokenBarrierException();//抛出broken异常
//如果当前线程被中断
if (Thread.interrupted()) {
breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
throw new InterruptedException();//抛出中断异常.
}
//index表示还需要几个线程调用await()方法,才能使得barrier发生trip
int index = --count;
if (index == 0) { //barrier发生trip的条件满足[tripped]
boolean ranAction = false;
try {
final Runnable command = barrierCommand;//barrier初始化时定义的任务
if (command != null)
command.run();//当前线程调用:barrier定义的任务,说明barrier定义的任务由最后到达barrier的线程来完成.
ranAction = true;//barrier的任务执行完成
nextGeneration();//唤醒所有线程;重置count和generation值.
return 0;
} finally {
if (!ranAction)//如果barrier任务执行失败,则generation属性broken设为true;重置count值;唤醒所有线程.
breakBarrier();
}
}
//一直自旋直到发生:tripped, broken, interrupted, timed out
for (;;) {
try {
if (!timed)//如果线程没有设置等待时间,则一直等待,直到其它线程将其唤醒
trip.await();
else if (nanos > 0L)//如果设置了超时时间,则等待制定时间.
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) { //自旋时被中断
if (g == generation && ! g.broken) { //如果其他线程还未出现问题导致当前barrier出现broken
breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
throw ie;
} else { //如果其他线程broken了当前barrier
//即使并未被中断,我们也会完成等待,因此这一中断被认作是"属于"后续的执行内容.
Thread.currentThread().interrupt();
}
}
//如果barrier被broken了,则抛出异常
if (g.broken)
throw new BrokenBarrierException();
//如果generation被重置,返回barrier在新一次的计数过程中,在可以trip之前还需要多少线程需要执行await()方法.
if (g != generation)
return index;
//如果线程设置了等待时间,且等时间<=0,抛出超时异常.
if (timed && nanos <= 0L) {
breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
throw new TimeoutException();
}
}
} finally {
lock.unlock();//解锁
}
}
/**
* 创建一个CyclicBarrier实例,参数parties指定了barrier上面的等待线程数.
* 当barrier发生trip时,由最后一个进入barrier的线程完成指定的barrier action.
*
* @param parties 发生trip之前,必须调用await()方法的线程数目.
* @param barrierAction 当barrier发生trip时,要执行的行为.如果没有需要执行的操作,此参数可以为null.
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
//不解释
public CyclicBarrier(int parties) {
this(parties, null);
}
//返回barrier需要发生trip的线程数目.
public int getParties() {
return parties;
}
/**
* 线程持续等待直到此barrier上的所有线程都调用了await()方法.
*
* 如果当前线程并不是到达的最后一个线程,则它被禁用线程调度目的,并且处于休眠状态,直到发生以下事件之一:
* 1.最后一个线程到达;
* 2.其他线程中断了当前线程.
* 3.其它线程中断了其它等待的线程.
* 4.在barrier上面等待的线程发生超时.
* 5.其它线程调用了barrier上面的reset方法.
*
* 如果当前线程:
* 1.在进入这一方法时,中断状态位被标记.
* 2.在等待过程中被中断
* 则会抛出中断异常InterruptedException,且当前线程的中断状态被清除.
*
* 会抛出BrokenBarrierException异常的情况有:
* 1.当其它线程在等待时,如果barrier被reset;
* 2.当调用await()方法时barrier发生了broken
*
* 任意等待线程发生了中断异常时,其它等待线程都会抛出BrokenBarrierException,且barrier的状态会变为broken.
*
* 如果当前线程是最后一个到达barrier的线程,且构造函数中的barrier action非null,则在其它线程可以继续执行前,当前线程会执行
* barrier action.
* 如果在barrier action的执行过程中发生了异常,则该异常会对当前线程产生影响,且barrier的会处于broken状态.
*
* @return 当前线程到达索引,第一个到达的索引值为:getParties() - 1;
* 最后一个到达的索引值为:0
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
/**
* 调用了dowait()方法,故细节可以到dowait()中查找.
* @param timeout 在barrier处等待的时间
* @param unit timeout参数的时间单位
*/
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
//查询当前barrier是否处于broken状态.
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
/**
* 将barrier状态重置.如果此时有线程在barrier处等待,它们会抛出BrokenBarrierException并返回.
* 注意:请注意,由于其他原因发生broken后重置可能会很复杂;线程需要通过一些方式来 完成同步,并选择一种方式完成reset.
* 相对为后续的使用重建一个barrier,此重置操作更受欢迎.
* 注意:这是一个需要加锁的操作.
*/
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
//返回barrier处等待的线程数.这一方法在debug和assert操作中很有用.
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
}