CyclicBarrier有什么作用?
假如我们有一个20个人的会议,需要等所有人到齐之后,领导才能致开场词,这个场景抽象出来就可以使用CyclicBarrier。20个人相当于20个子线程,领导致辞相当于另一个主线程,每当一个子线程启动,计数加1,最后计数到20时表示人全部到齐,可以执行主线程,领导开始致辞!@#¥%……&……………. 好了 ,领导致辞完毕,20个人一起开始噼里啪啦鼓掌。。。。。。
以下为对CyclicBarrier的分析
变量:
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();//使用lock控制线程安全
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();//condition用来阻塞或者唤醒线程
/** The number of parties */
private final int parties;//需要线程个数
/* The command to run when tripped */
private final Runnable barrierCommand;//最后满足条件需要执行的主线程操作(可以不需要)
/** The current generation */
private Generation generation = new Generation();//Generation表示分代,每一组parties表示一代
private int count;//计数时使用
构造方法:
一共有两个构造方法,其区别在于Runnable barrierAction,就是一个可执行的线程类,用于一些复杂的业务,比如,可以代表领导讲话......
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
核心方法await():
两个await方法,可以选择等待一定的时间
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
草鸡重要的方法await实际执行的方法:dowait方法
/**
* Main barrier code, covering the various policies.
*/
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()) {
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();
}
}
// 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 {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
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();
}
}
该方法其大概执行流程是:如果等待中的线程有执行中断,或者对barrier执行reset操作,那么将会抛出相应的异常。。。。。。
其实await()的处理逻辑还是比较简单的:如果该线程不是到达的最后一个线程,则他会一直处于等待状态,除非发生以下情况:
最后一个线程到达,即index == 0
超出了指定时间(超时等待)
其他的某个线程中断当前线程
其他的某个线程中断另一个等待的线程
其他的某个线程在等待barrier超时
其他的某个线程在此barrier调用reset()方法。reset()方法用于将屏障重置为初始状态。(引用大神的原文:http://cmsblogs.com/?p=2241)
不出异常情况下:
1.如果当前count==0,说明线程数量已经够了,可以唤醒大boss了,即执行command.run(),当然我们在构造函数中可以不传递command,也就是没有大boss。但是nextGeneration()方法一定会执行,该方法主要是唤醒被condition.await()的线程,然后重新创建一个generation,新的一代barrier就这样产生了。。。。。。
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
2.当前count!=0,很简单,执行当前线程阻塞操作,通过conditon.await()实现。
简单案例:
/**
* cycliBarrier会等待线程数量达到某一个值的时候才会继续往下执行,不满足该值的话该线程会阻塞
* 假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,此时就可以利用CyclicBarrier了
*/
public class CyclicBarrierLearn {
public static void main(String[] args) {
int N = 20;
CyclicBarrier barrier = new CyclicBarrier(N, new Thread() {
@Override
public void run() {
System.out.println("领导开始发言了,呱唧呱唧呱唧");
}
});
for (int i = 0; i < N; i++) {
new Employee(barrier).start();
}
}
static class Employee extends Thread {
private CyclicBarrier cyclicBarrier;
public Employee(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("员工" + Thread.currentThread().getName() + "签到完毕,等待领导讲话");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("领导讲完话了,"+Thread.currentThread().getName()+"鼓掌中...");
}
}
}
结果
员工Thread-1签到完毕,等待领导讲话
员工Thread-3签到完毕,等待领导讲话
员工Thread-5签到完毕,等待领导讲话
员工Thread-9签到完毕,等待领导讲话
员工Thread-7签到完毕,等待领导讲话
员工Thread-11签到完毕,等待领导讲话
员工Thread-4签到完毕,等待领导讲话
员工Thread-13签到完毕,等待领导讲话
员工Thread-15签到完毕,等待领导讲话
员工Thread-2签到完毕,等待领导讲话
员工Thread-19签到完毕,等待领导讲话
员工Thread-17签到完毕,等待领导讲话
员工Thread-20签到完毕,等待领导讲话
员工Thread-16签到完毕,等待领导讲话
员工Thread-12签到完毕,等待领导讲话
员工Thread-18签到完毕,等待领导讲话
员工Thread-8签到完毕,等待领导讲话
员工Thread-10签到完毕,等待领导讲话
员工Thread-14签到完毕,等待领导讲话
员工Thread-6签到完毕,等待领导讲话
领导开始发言了,呱唧呱唧呱唧
领导讲完话了,Thread-6鼓掌中...
领导讲完话了,Thread-1鼓掌中...
领导讲完话了,Thread-5鼓掌中...
领导讲完话了,Thread-3鼓掌中...
领导讲完话了,Thread-9鼓掌中...
领导讲完话了,Thread-7鼓掌中...
领导讲完话了,Thread-11鼓掌中...
领导讲完话了,Thread-4鼓掌中...
领导讲完话了,Thread-13鼓掌中...
领导讲完话了,Thread-15鼓掌中...
领导讲完话了,Thread-2鼓掌中...
领导讲完话了,Thread-19鼓掌中...
领导讲完话了,Thread-17鼓掌中...
领导讲完话了,Thread-16鼓掌中...
领导讲完话了,Thread-20鼓掌中...
领导讲完话了,Thread-12鼓掌中...
领导讲完话了,Thread-18鼓掌中...
领导讲完话了,Thread-8鼓掌中...
领导讲完话了,Thread-10鼓掌中...
领导讲完话了,Thread-14鼓掌中...
总结:CyclicBarrier中的核心方法就是dowait()方法,使用lock+condition的方式保证线程安全,然后使用一个count计数器来记录是否达到条件。总结的不到位,希望大家批评指正。。。。。。