示例代码
public class TestCyclicBarrier {
public static CyclicBarrier barrier = new CyclicBarrier(4,()-> {
System.out.println(Thread.currentThread().getName()+"dododo");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("dododo end");
});
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
System.out.println("开启创建线程");
for (int i=0;i<3;i++){
System.out.println("创建线程 " +i);
new Thread(new Tasks()).start();
}
System.out.println("Manin 等待...");
barrier.await();
System.out.println("Main 等待结束");
System.out.println("结束");
}
}
class Tasks implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" 完成任务,开始等待 ");
TestCyclicBarrier.barrier.await();
System.out.println(Thread.currentThread().getName()+" 结束等待 ");
System.out.println(Thread.currentThread().getName()+" 结束 ");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
原理
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//每个调用await的线程都会先获取锁执行下面的代码
lock.lock();
try {
//用于reset重置,Generation 中只有一个boolean类型的break标志用于标志当前屏障是否已经被打破
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();//打破屏障
throw new InterruptedException();
}
//计算当前await数量
int index = --count;
//最后一个线程调用await
if (index == 0) { // tripped
boolean ranAction = false;
try {
//最后一个线程会执行创建屏障时传入的优先任务,这个任务的运行是同步的
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//这里会将所有等待在Condition上的线程移到同步队列中去,并设置新的年代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//对于前面调用await的线程会进行到这里
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();
}
}
//当最后一个线程调用了await后,所有被阻塞的线程会一个一个的醒来
if (g.broken)
throw new BrokenBarrierException();
//最后一个线程那里已经设置了新年代,这里会返回index
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//返回的最后一步释放锁,同时会唤醒同步队列中的下一个等待节点。
lock.unlock();
}
}
流程
前提:四个线程,我们分别按照1,2,3,4代表他们执行到await函数的顺序
- 线程1,2,3计算的
index !=0
,因此会进入到trip.await()
,即进入到Condition
的等待队列中,进入等待队列中的时候会线程阻塞并释放lock
锁,只有线程1释放锁后线程2,3才能做该步骤的操作 - 线程4计算
index=0
,进入条件语句执行优先任务,执行完后将设置年代为下一代并将等待队列中所有的线程移到同步队列中 - 线程4从
TestCyclicBarrier.barrier.await()
返回执行后面的任务 - 线程1,2,3被唤醒进行争抢lock锁,得到的锁的从
trip.await()
返回,没抢到的走AQS同步队列那一套。这里假设线程1抢到了锁,后面判断年代发现年代更新,则说明本轮已结束,返回到TestCyclicBarrier.barrier.await();
之前在finally
中释放lock
锁并唤醒同步队列下一个节点 - 线程2,3以此类推
对于手动打破屏障breakBarrier();
的调用基本都是在中断、超时异常等情况才会设置该标志