相关阅读
- JUC源码简析 AbstractQueuedSynchronizer
- JUC源码简析 Condition
- JUC源码简析 CountDownLatch
- JUC源码简析 ReentrantLock
- JUC源码简析 Semaphore
简介
CyclicBarrier
表示可循环使用栅栏,即一个可循环利用的屏障,可以让所有线程彼此等待,直到所有线程都完成相应任务后,然后屏障前的所有线程才继续下一步动作;
内部持有的ReentrantLock
的实例,保护屏障的入口;持有的Condition
的实例,充当屏障;
和CountDownLatch的比较
CountDownLatch
是线程组之间的等待,即一个(或多个)线程等待其它N个线程完成相应任务后再执行下一步;而CyclicBarrier
是线程组内的等待,即组内的每个线程相互等待,等都到达屏障,再执行下一步;CountDownLatch
计数为0时无法重置,即无法循环使用;而CyclicBarrier
计数达到初始值时,可以重置,即可以循环使用;
源码简析
内部类——Generation
private static class Generation {
boolean broken = false;
}
维护等待线程的状态;
CyclicBarrier
构造方法
public CyclicBarrier(int parties, Runnable barrierAction) {
// parties表示线程组中线程的个数,即要推倒栅栏(执行await方法)的线程个数,必须大于0
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
// 栅栏推倒后需要执行的任务,可以为null
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
nextGeneration
private void nextGeneration() {
// 调用Condition的signalALL方法,唤醒所有等待线程
trip.signalAll();
// 重设count为parties
count = parties;
// 重设generation成员
generation = new Generation();
}
本方法只能在持有锁的时候调用;
breakBarrier
private void breakBarrier() {
// 设置generation状态为“推倒”
generation.broken = true;
// 重设count为parties
count = parties;
// 调用Condition的signalALL方法,唤醒所有等待线程
trip.signalAll();
}
本方法只能在持有锁的时候调用;
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)
// 此时栅栏状态已经是“推倒”,则抛出BrokenBarrierException
throw new BrokenBarrierException();
// 如果当前线程发生过中断
if (Thread.interrupted()) {
// 调用breakBarrier推倒栅栏
breakBarrier();
// 抛出InterruptedException
throw new InterruptedException();
}
int index = --count;
// 如果栅栏的等待线程数量为0,说明最后一个线程达到栅栏,可以推倒栅栏了
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
// 推倒栅栏前有任务执行,则执行该任务
command.run();
// 设置推倒栅栏成功标志为true
ranAction = true;
// 开启下一循环,可循环使用的体现
nextGeneration();
return 0;
} finally {
// 当执行推倒栅栏后的任务发生异常时进入
if (!ranAction)
// 若还未推倒栅栏,则推倒栅栏
breakBarrier();
}
}
// 死循环执行,确保本线程挂住,除非栅栏被推倒,或者本线程发生中断或者超时
for (;;) {
try {
if (!timed)
// 不关心超时,则调用Condition.await释放锁资源,进行等待状态
trip.await();
else if (nanos > 0L)
// 若关心超时,且超时时间大于0,则调用Condition.awaitNanos释放锁资源,同时设置了超时时间,进行等待状态等待
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
// 本线程在等待过程中发生中断异常
// 如果当前栅栏信息没变化且栅栏还没被推倒
if (g == generation && ! g.broken) {
// 推倒栅栏,因为等待发生中断,栅栏就失效了
breakBarrier();
// 继续抛出InterruptedException
throw ie;
} else {
// 虽然本线程发生了中断,但栅栏信息已经变化
// 1. 变成新栅栏,本线程不属于新栅栏,不会对新栅栏有影响
// 2. 栅栏已被推倒,那么即使本线程未发生中断,也会被其它线程推倒栅栏时唤醒
// 所以此中断没啥作用,只需要重新设置中断标志即可
Thread.currentThread().interrupt();
}
}
// 1. 可能是被其它推倒栅栏的线程调用signalAll唤醒;
// 2. 可能是本线程发生中断异常唤醒;
// 3. 可能是本线程设置超时时间,超时导致唤醒;
// 4. 可能是本线程设置超时时间不大于0
if (g.broken)
// 如果记录的栅栏为“推倒”状态,则抛出BrokenBarrierException
throw new BrokenBarrierException();
if (g != generation)
// 如果栅栏信息变化,则表示因为其它线程执行换代操作而唤醒本线程
// 则直接返回原先记录的栅栏的等待线程数量(当前线程所在栅栏的下标)
return index;
if (timed && nanos <= 0L) {
// 线程关心超时且发生了超时,则栅栏失效了
// 推倒栅栏
breakBarrier();
// 抛出TimeoutException
throw new TimeoutException();
}
}
} finally {
// finally代码块确保lock被释放
lock.unlock();
}
}
await
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
// 未设置超时时间却发生超时异常,则抛出错误
throw new Error(toe);
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
isBroken
public boolean isBroken() {
final ReentrantLock lock = this.lock;
// 先上锁
lock.lock();
try {
// 返回当前栅栏的状态
return generation.broken;
} finally {
// finally代码块确保lock被释放
lock.unlock();
}
}
reset
public void reset() {
final ReentrantLock lock = this.lock;
// 先上锁
lock.lock();
try {
// 推倒栅栏
breakBarrier();
// 开启新的栅栏
nextGeneration();
} finally {
// finally代码块确保lock被释放
lock.unlock();
}
}
getNumberWaiting
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
// 先上锁
lock.lock();
try {
return parties - count;
} finally {
// finally代码块确保lock被释放
lock.unlock();
}
}
测试
Demo
class Demo {
private int parties = 5;
private CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("推倒栅栏");
}
});
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
ExecutorService es = Executors.newFixedThreadPool(demo.parties);
for (int i=0; i<demo.parties; i++) {
es.execute(new CyclicBarrierTask(demo.cyclicBarrier));
}
es.shutdown();
}
private static class CyclicBarrierTask implements Runnable {
private CyclicBarrier cb;
CyclicBarrierTask(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
int threadIndex = 0;
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " arrive to barrier at first time");
threadIndex = cb.await();
System.out.println(Thread.currentThread().getName() + " at index " + (cb.getParties()-threadIndex) + " of the barrier breaks barrier at first time");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " arrive to barrier at twice time");
threadIndex = cb.await();
System.out.println(Thread.currentThread().getName() + " at index " + (cb.getParties()-threadIndex) + " of the barrier breaks barrier at twice time");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
测试结果
pool-1-thread-2 arrive to barrier at first time
pool-1-thread-1 arrive to barrier at first time
pool-1-thread-4 arrive to barrier at first time
pool-1-thread-3 arrive to barrier at first time
pool-1-thread-5 arrive to barrier at first time
推倒栅栏
pool-1-thread-5 at index 5 of the barrier breaks barrier at first time
pool-1-thread-3 at index 4 of the barrier breaks barrier at first time
pool-1-thread-4 at index 3 of the barrier breaks barrier at first time
pool-1-thread-1 at index 2 of the barrier breaks barrier at first time
pool-1-thread-2 at index 1 of the barrier breaks barrier at first time
pool-1-thread-1 arrive to barrier at twice time
pool-1-thread-4 arrive to barrier at twice time
pool-1-thread-5 arrive to barrier at twice time
pool-1-thread-3 arrive to barrier at twice time
pool-1-thread-2 arrive to barrier at twice time
推倒栅栏
pool-1-thread-2 at index 5 of the barrier breaks barrier at twice time
pool-1-thread-4 at index 2 of the barrier breaks barrier at twice time
pool-1-thread-1 at index 1 of the barrier breaks barrier at twice time
pool-1-thread-5 at index 3 of the barrier breaks barrier at twice time
pool-1-thread-3 at index 4 of the barrier breaks barrier at twice time