关联文章:
关联文章:
Java并发源码分析之AQS及ReentrantLock
Java并发源码分析之Semaphore
Java并发源码分析之ReentrantReadWriteLock
Java并发源码分析之Condition
Java并发源码分析之CountDownLatch
目录
工作原理概要
CyclicBarrier内部持有ReentrantLock和Condition对象,定义一个资源的值,然后开启与资源值相同数量的线程,线程运行时,调用await()方法,形成屏障,先到达屏障的线程会被阻塞,加入等待队列中等待。等到所有线程都到达屏障位置,等待的线程会被唤醒,才能继续执行。
使用demo
public class CyclicBarrierRunnable implements Runnable {
private CyclicBarrier cyclicBarrier;
public CyclicBarrierRunnable(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
System.out.println("线程: " + Thread.currentThread().getName() + "开始执行任务, Time:" + LocalDateTime.now());
int millis = (int) (Math.random() * 5000);
Thread.sleep(millis);
System.out.println("线程: " + Thread.currentThread().getName() + "执行任务完成, Time:" + LocalDateTime.now());
this.cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程: " + Thread.currentThread().getName() + "退出, Time:" + LocalDateTime.now());
}
}
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
for (int i = 0; i < 10; i++) {
executor.execute(new CyclicBarrierRunnable(cyclicBarrier));
}
}
}
开启了10个线程,每个线程执行的时间不同,最后会统一时间退出。
运行结果为:
可以看到最后的退出时间一致的。
变量
private final ReentrantLock lock = new ReentrantLock();
// 等待队列
private final Condition trip = lock.newCondition();
// 资源值数量
private final int parties;
// 所有线程到达屏障时,执行该run方法
private final Runnable barrierCommand;
// 纪元,CyclicBarrier支持重复使用
private Generation generation = new Generation();
// 计数器,剩余未到达屏障的线程数量
private int count;
构造函数
//
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
// parties:资源值数量
public CyclicBarrier(int parties) {
this(parties, null);
}
暂停执行方法解析
1、await()暂停执行入口
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe);
}
}
2、dowait()暂停执行
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()) {
// 中断Barrier
breakBarrier();
// 抛出异常
throw new InterruptedException();
}
// 当前线程已经到达屏障, 将计数器-1
int index = --count;
// 判断是否达到临界状态, 即所有线程都已经到达屏障, count = 0
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
// 检查是否有额外动作要执行
if (command != null)
// 执行额外操作
command.run();
ranAction = true;
// 进入下一个纪元, 方法内部有调用signalAll唤醒所有等待的线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
// 关闭屏障
breakBarrier();
}
}
// 如果没有达到临界状态, 则开始阻塞
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 {
// 走到这里说明,已经进入下一个纪元或者屏障被打破,中断线程
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();
}
}
3、breakBarrier()打破屏障
private void breakBarrier() {
// 设置纪元的状态为破坏状态
generation.broken = true;
// 重置计数器
count = parties;
// 唤醒所有等待队列中的线程
trip.signalAll();
}
4、nextGeneration()进入下个纪元
// 开启下个纪元, 重置所有参数
private void nextGeneration() {
// 唤醒所有等待队列中的线程
trip.signalAll();
// 重置计数器
count = parties;
// 生成一个新的纪元
generation = new Generation();
}