CyclicBarrier
按照字面意思是环绕栅栏,它的作用的让所有的线程集合到一起,然后一起执行。它与CountDownLatch
的区别是:CountDownLatch
是一个线程等待其他线程完成后它才进入运行状态;CyclicBarrier
是线程之间的相互等待,只有所有的线程都到达栅栏后才一起执行。如下图,线程1、线程2、线程3在栅栏前等待全部线程到齐才运行:
例子
public class CyclicBarrierDemo {
static class Soldier implements Runnable {
CyclicBarrier cyclicBarrier;
int index;
Soldier (CyclicBarrier cyclicBarrier,Integer index){
this.cyclicBarrier = cyclicBarrier;
this.index = index;
}
@Override
public void run() {
try {
System.out.println("士兵"+index+"已经集合");
//等待其他士兵集合
cyclicBarrier.await();
//全部士兵集合后,开始各种完成自己的任务
Thread.sleep(Math.abs(new Random().nextInt() % 10000));
System.out.println("士兵"+index+"已经完成了任务");
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int numbers = 5;
CyclicBarrier cyclicBarrier = new CyclicBarrier(numbers, new Runnable() {
//栅栏放行前的任务
@Override
public void run() {
System.out.println("全部士兵已经集合,开始执行任务");
}
});
for(int i = 1; i <= numbers; i++){
new Thread(new Soldier(cyclicBarrier,i)).start();
}
}
}
执行结果:可以看到执行结果,只有当所有的线程都到达栅栏后,栅栏才会放开,然后线程各自去获取锁执行任务。
士兵1已经集合
士兵3已经集合
士兵2已经集合
士兵4已经集合
士兵5已经集合
全部士兵已经集合,开始执行任务
士兵1已经完成了任务
士兵2已经完成了任务
士兵5已经完成了任务
士兵4已经完成了任务
士兵3已经完成了任务
源码分析
构造方法
CyclicBarrier
是一个循环栅栏,parties
规定栅栏新一轮期需要等待线程数量;barrierAction
所有线程到达后,栅栏的回调方法;count
表示当前轮期栅栏还需要等待多少线程才能放开。
CyclicBarrier
是一个循环栅栏,每一轮执行完之后,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);
}
成员变量
//独占锁,非公平模式
private final ReentrantLock lock = new ReentrantLock();
//条件,用来存储栅栏放开前阻塞的线程,当线程全部到达后,调用trip.signalAll()唤醒所有的线程
private final Condition trip = lock.newCondition();
//循环栅栏的初始等待线程数量
private final int parties;
//所有的线程到达后栅栏的回调方法,barrierCommand可以为空
private final Runnable barrierCommand;
//当前栅栏是否被中断,每一轮generation都会重新创建
private Generation generation = new Generation();
//循环栅栏当前需要等待多少线程才放行,count==0栅栏才放行
private int count;
await 阻塞线程
线程调用await
方法来等待其他线程到齐,如果此时线程已经全部到齐,那么会调用Condition
的signalAll
方法来唤醒阻塞线程。
public int await() throws InterruptedException, BrokenBarrierException {
try {
//线程进入阻塞
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
await
会调用dowait
方法。dowait
方法有两个参数:timed
用来设置线程是否有等待超时机制,如果有,那么线程会阻塞nanos
毫秒后超时。
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//1.获取独占锁
lock.lock();
try {
//栅栏的当前轮次的状态
final Generation g = generation;
//2.判断栅栏是否被破坏,如果被破坏将抛出异常
if (g.broken)
throw new BrokenBarrierException();
//判断线程是否被中断
if (Thread.interrupted()) {
//如果线程被中断,将栅栏的状态设置为被破坏
breakBarrier();
throw new InterruptedException();
}
//index表示当前栅栏还需要等待多少线程到达
int index = --count;
//index =0 表示所有的线程已经到达栅栏
if (index == 0) { // tripped
//3.如果栅栏等待线程都已经到达
boolean ranAction = false;
try {
//执行栅栏的回调方法
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//唤醒所有的线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//4.栅栏等待线程没有全部到达
for (;;) {
try {
if (!timed)
//将当前线程存储到Condition等待队列,阻塞线程
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();
}
}
-
获取独占锁。
CyclicBarrier
因为使用到了Condition
,所以需要先调用ReentrantLock
的lock
方法来获取锁。 -
检测栅栏是否被破坏
CyclicBarrier
支持线程中断,所以如果有线程被中断后,它除了自身抛出异常外,还会将栅栏的状态设置为被破坏,然后唤醒其他的线程。//线程被中断 if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } private void breakBarrier() { //设置栅栏状态 generation.broken = true; //重设count count = parties; //唤醒线程,这些线程将被添加到独占锁的等待队列中去尝试获取锁 trip.signalAll(); }
阻塞的线程被唤醒后,检测到栅栏被破坏,也将抛出异常。
if (g.broken) throw new BrokenBarrierException();
-
线程全部到齐
如果线程已经全部到齐后,在打开栅栏前执行回调任务。
final Runnable command = barrierCommand; if (command != null) command.run(); ranAction = true; //唤醒所有的线程 nextGeneration();
执行完
barrierCommand
任务后,将唤醒所有的阻塞线程,让线程各自去完成自己的任务。private void nextGeneration() { //唤醒所有的任务 trip.signalAll(); // set up next generation count = parties; //重新设置Generation,新一轮的栅栏期 generation = new Generation(); }
-
线程没有全部到达
如果线程没有到,那么将调用
Condition
的await
方法,将当前线程保存到Condition
的阻塞队列,阻塞当前线程。Condition
的源码分析参考Condition源码分析。
if (!timed)
//一直阻塞,直到所有的线程都到达或者被中断
trip.await();
else if (nanos > 0L)
//设置超时等待
nanos = trip.awaitNanos(nanos);