一、简介
CyclicBarrier :字面翻译为 【可重复使用的栅栏】。实现一组线程相互等待,当所有线程都到达某个屏障点后再进行后续的操
二、类图
Generation :当有parties个线程到达barrier之后,generation就会被更新换代。
三、工作示意图
四、源码解析
1、构造函数
parties 表示 屏障拦截的线程数; barrierAction 表示 所有线程都到达屏障时 要处理的业务
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
2、await()—阻塞当前线程直到所有的线程都到达屏障
(1)种类:2种(带超时时间、 不带超时时间)
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));
}
(2)内部流程:dowait(),主要的屏障方法
如果当前线程不是最后到达的线程,处于等待状态,直到发生以下情况之一:
- 最后一个线程到达
- 某个线程等待中断
- 某个线程等待超时
- 调用了CyclicBarrier的reset()方法。该方法会将屏障重置为初始状态
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()) {
/**
1、 将损坏状态broken设置为true
2、 唤醒其他阻塞在此栅栏上的线程
**/
breakBarrier();
throw new InterruptedException();
}
//每次内部计数-1
int index = --count;
//index==0表示最后一个线程到达屏障
if (index == 0) { // tripped
boolean ranAction = false;
try {
//唤醒所有线程前先执行指定的任务
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
/**
1、唤醒所有线程
2、重新设置计数器
3、重新设置栅栏代次
**/
nextGeneration();
return 0;
} finally {
//栅栏任务执行失败: broken设为true;唤醒全部等待线程
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 {
//已换代,即已完成在栅栏上的等待,即在最后一次线程执行signalAll后,并且更新了这个“代”对象。在这个区间,这个线程被中断了.JDK认为任务已经完成了,只需打个标记
Thread.currentThread().interrupt();
}
}
// 当有任何一个线程中断了,就会调用breakBarrier方法
// 就会唤醒其他的线程,其他线程醒来后,也要抛出异常【因打破栅栏被唤醒则抛异常】
if (g.broken)
throw new BrokenBarrierException();
// 如果 g == generation,说明还没有换代,那为什么会醒了?
// 因为一个线程可以使用多个栅栏,当别的栅栏唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。
// 正是因为这个原因,才需要generation来保证正确。
//【因为换代而被唤醒则返回计数器的值】
if (g != generation)
return index;
//【如果线程因为超时而被唤醒则打翻栅栏并抛出异常】
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放独占锁
lock.unlock();
}
}
breakBarrier()—打破屏障
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
nextGeneration()—更新换代
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
问题:什么情况会线程会抛出 BrokenBarrierException 异常?
- 其他线程调用reset()
- 一个线程被中断,调用 breakBarrier()唤醒其他线程时,其他线程抛出
3、reset()—重置栅栏
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
五、DEMO—人满发车
//乘客类
public class Passenger implements Runnable {
private static CyclicBarrier barrier;
private String name;
public Passenger(String n, CyclicBarrier b) {
name = n;
barrier = b;
}
@Override
public void run() {
try {
System.out.println(name + "已经上车");
barrier.await();
// 工作线程开始处理,这里用Thread.sleep()来模拟业务处理
Thread.sleep(100);
System.out.println("乘车一段时间后," + name + "下车");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CarMove implements Runnable {
/**
* 座位数
*/
private static final int SEAT = 3;
/**
* 每天发车次数 计数
*/
private static int count = 0;
/**
* 人满发车
*/
@Override
public void run() {
count++;
System.out.println("人满,第" + count + "次发车");
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(SEAT, new CarMove());
new Thread(new Passenger("1111", barrier)).start();
new Thread(new Passenger("2222", barrier)).start();
new Thread(new Passenger("3333", barrier)).start();
//这一步是为了观察不同线程组的运行情况
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Passenger("4444", barrier)).start();
new Thread(new Passenger("5555", barrier)).start();
new Thread(new Passenger("6666", barrier)).start();
}
}
参考资料:
【深入理解CyclicBarrier原理】https://blog.csdn.net/qq_38293564/article/details/80558157