读前必看ReentrantLock——http://blog.csdn.net/qq_31957747/article/details/74929911
一、循环栅栏CyclicBarrier
CyclicBarrier它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。比如将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归0,然后接着凑齐下一批10个线程,这就是循环栅栏内在的含义。
CountDownLatch和CyclicBarrier的区别:
1、 CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。2、 CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用。
构造方法:
CyclicBarrier(int parties)CyclicBarrier(int parties, Runnable barrierAction)
其中parties表示计数总数,也就是参与的线程总数。barrierAction表示当计数器一次计数完成后,系统会执行的动作。
主要方法:
int await():在所有参与线程都已经在此 barrier 上调用 await 方法之前,将一直等待。int await(long timeout, TimeUnit unit):在所有参与线程都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
int getNumberWaiting():返回当前在屏障处等待的参与者数目。
int getParties():返回要求启动此 barrier 的参与线程数目。
boolean isBroken():查询此屏障是否处于损坏状态。
void reset():将屏障重置为其初始状态。
例子:
司令官让士兵集合完成任务的场景
public class CyclicBarrierDemo {
public static class Soldier implements Runnable{
private String soldierName;
private final CyclicBarrier cyclic;
Soldier(CyclicBarrier cyclic,String soldierName){
this.cyclic = cyclic;
this.soldierName = soldierName;
}
@Override
public void run() {
try{
//等待其他士兵到齐
cyclic.await();
doWork();
//等待所有士兵完成工作
cyclic.await();
}catch (InterruptedException e){
e.printStackTrace();
}catch (BrokenBarrierException e){
e.printStackTrace();
}
}
void doWork(){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(soldierName+" :任务完成");
}
}
public static class BarrierRun implements Runnable{
boolean flag;
int N;
public BarrierRun(boolean flag,int N){
this.flag = flag;
this.N = N;
}
@Override
public void run() {
if(flag){
System.out.println("司令:[士兵"+N+"个,任务完成!]");
}else{
System.out.println("司令:[士兵"+N+"个,集合完毕!]");
flag = true;
}
}
}
public static void main(String[] args) {
final int N = 10;
Thread[] allSoldier = new Thread[N];
boolean flag = false;
CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(flag,N));
//设置屏障点,主要是为了执行这个方法
System.out.println("集合队伍!");
for(int i = 0;i < N;i++){
System.out.println("士兵"+i+" 报道!");
allSoldier[i] =new Thread(new Soldier(cyclic,"士兵"+i));
allSoldier[i].start();
}
}
}
运行结果:
可以看到这段代码10个士兵线程,先后到达了2次屏障点,每次到达公共屏障点之后执行了BarrierRun的run方法,先后输出了集合完毕和任务完成。重点在于每个士兵都相互等待,直到所有的士兵一起加入,才进行下一步。
二、CyclicBarrier源码分析
与之前介绍的同步类不同,CyclicBarrier是用ReentrantLock跟Condition来实现的。
首先看他的成员变量:
private static class Generation {
boolean broken = false;
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition();
//参与线程的数量
private final int parties;
//所有参与线程到达屏障点后执行的动作
private final Runnable barrierCommand;
//当前的generation,同一批线程属于同一个generation,每跨越一次屏障点就换一个新的generation
private Generation generation = new Generation();
//还在等待的线程个数(注意:这里的等待不是condition队列中的等待线程,而是还没执行await()方法的线程,得明白每个等待的含义)
private int count;
构造方法:
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);
}
public int await() throws InterruptedException, BrokenBarrierException {
try {
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(); //拿到lock锁
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
//如果线程被中断,就通过breakBarrier()破坏掉当前的generation,并唤醒所有的在condition队列中等待的线程
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//还在等待的线程数-1
int index = --count;
if (index == 0) { //所有线程均调用了await(),即到达公共屏障点
boolean ranAction = false; //用来判断到达屏障点之后执行的动作是否正常执行
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();//注意这里是run()而不是start(),也就是说是同步
ranAction = true;
nextGeneration();//一次屏障跨越,”更新换代”,即唤醒所有的condition队列中的线程,并把count置为parties,然后换一个新的generation
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) { //自旋
try {
if (!timed)
trip.await();//将当前线程加入到condition队列,释放lock锁资源,使得下一个线程能获得锁资源进入lock块
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//等待过程线程被中断
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
// 如果“当前generation已经损坏”,则抛出异常。
if (g.broken)
throw new BrokenBarrierException();
// 如果“generation已经更新换代”,则返回index。
if (g != generation)
return index;
//”定时等待”且时间已到,则终止cyclicBarrier,唤醒condition队列中所有的线程
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放lock锁
lock.unlock();
}
}
再看上面代码提到的breakBarrier()和nextGeneration()方法:
private void breakBarrier() {
generation.broken = true; //设置当前generation被破坏
count = parties; //重置count
trip.signalAll(); //唤醒condition队列中的所有等待线程
}
private void nextGeneration() {
trip.signalAll(); //唤醒condition队列中的所有等待线程
count = parties; //重置count
generation = new Generation(); //”更新换代”
}
总结:
CyclicBarrier的关键在于利用ReentrantLock和Condition,每个调用await()方法的线程都被加入到condition队列中进行等待,所有参与线程都调用了await()之后,执行设置的后续动作barrierCommand,再唤醒condition队列中的所有等待线程,重置count,并"更新换代"。