理解CyclicBarrier
认识CyclicBarrier
CyclicBarrier也叫同步屏障,循环栅栏(后续专门讲解"循环"),可以让一组线程到达屏障时被阻塞,直到最后一个线程到达后,他们一起被执行.
CyclicBarrier好比一扇门,默认情况下关闭状态,直到所有线程都就位,门才打开,使其通行,
源码分析
public class CyclicBarrier {
private static class Generation {
boolean broken = false;
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition();
private final int parties;
private int count
private final Runnable barrierCommand;
private Generation generation = new Generation();
...
}
下面介绍下CyclicBarrier的字段
- lock: CyclicBarrier是基于ReentrantLock实现的
- Generation :代,栅栏的年代/年龄
- generation 当线程全部就位,栅栏每次打开 或者 栅栏重置reset后,年代改变
- trip:lock上的Condition条件,当线程未全部就位时,到达栅栏的线程将被添加到该条件队列
- parties 常量,代表线程的数量,在构造时传入,当前就位线程数(count)==parties时,栅栏打开
- count 当前就位的线程数.当count==parties时,栅栏打开
- barrierCommand command线程,在所有线程就位之后且未被唤醒 期间 执行
构造方法
- CyclicBarrier(int parties),
- CyclicBarrier(int parties, Runnable barrierAction)
介绍完字段,一起看await核心实现(doawait(false,oL)):
/**
* 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();
}
}
- 准备工作:
当一个线程到达时,首先看generation是否被破坏,如果没被破坏,检查当前线程是否中断,如果中断,则该线程无法就位,这组线程将全部不能执行,执行breakBarrier()
2. 内部变量count减1,如果count等于0,调用command线程的run,并且执行nextGeneration();
其中nextGeneration()方法使得栅栏可循环使用:
a. 唤醒所以就位线程
b. 重新设置栅栏 :count恢复为parties,重新生成Generation对象,赋值给generation
3. 如果count不等于0,说明有线程还未到屏障处,则在锁条件变量trip上等待。
接下来看看nextGeneration方法
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
到这里,CyclicBarrier的原理已经就介绍完了,最后看下它的应用场景.
应用场景
假设我们程序员小组(20个人)要去体检,当所有程序员就位后,一同出发
Programmer.java :每个运动员都就位后才开始。
class Programmer implements Runnable {
private CyclicBarrier cyclicBarrier;
private String name;
public Programmer(CyclicBarrier cyclicBarrier, String name) {
this.cyclicBarrier = cyclicBarrier;
this.name = name;
}
@Override
public void run() {
System.out.println(name + "就位");
try {
cyclicBarrier.await();
System.out.println(time + ": "+ new Date());
} catch (Exception e) {
}
}
}
HealthCheck.java : 负责屏障的初始化。
class HealthCheck {
private CyclicBarrier cyclicBarrier = new CyclicBarrier(20);
public void start() {
List<Programmer> programmers = new ArrayList<>();
for (int i=0;i<20;i++){
programmers.add(new Programmer(cyclicBarrier,"programmer"+"-"+i));
}
Executor executor = Executors.newFixedThreadPool(8);
for (Programmer programmer : programmers) {
executor.execute(programmer);
}
}
}
编辑于 2019-02-20 15:29