CyclicBarrier 是可以复用的 ,但是如果出现 等待超时, 或者 出现每轮 执行完
执行的对应的 runnable 任务 出现异常 那么后续 线程 统统都会抛出异常
public int await() throws InterruptedException, BrokenBarrierException {
try {
// go to
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
// 真正分析的方法 重点
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 这个 类对象 是一个 标记对象 默认是fasle
// 每轮 达到的线程 满足条件后 generation 会=new generation()
// 从而代表一轮 结束
// 如果出现超时,每轮的刷新时之前 执行的任务 出现异常 那么 后续
// generation 的成员变量=true 那么就会让后续 执行 await的线程
// 中断执行了 thoew Erro
final Generation g = generation;
// 预判
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 每次有线程进来就 -- 如果=0 那么代表这个线程是这一轮的最后
// 一个线程 那么需要唤醒 之前在 condition上等待的Node 线程
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
// 如果正常执行完 任务 或者没任务 把 标记值=true
// 通过这个值来判断是否发生异常
ranAction = true;
//这个方法 主要是 把 在 条件队列上等待的线程 移到
// clh 队列之中 然后等待 每轮的最后一个线程 释放锁时
// 唤醒 headNode.nextNode的线程
// 从这个角度看 虽然 new ReentranLock 是非公平
// 但是由于 当前这个机制下 其实嗨是公平的 枪锁机制
// nextNode 永远被headNode 的线程 Unpark
nextGeneration();
return 0;
} finally {
// 出现异常 也会将等待的Node 移到Clh队列之中 等待 unlock
// 时唤醒 只不过 那个值=true 唤醒之后 直接抛出异常
if (!ranAction)
breakBarrier();
}
}
//不是每轮的 尾部线程 就会在这儿 自旋[基本上用不上 第一次就被park]
for (;;) {
try {
if (!timed)
//如果没设置超时 那么直接 等待 下面就是加了个超时等待
// 超时等待 只不过 会在 park 加个参数 到时自动唤醒
// 如果小于1s 不会park 直接where 一直等待 超时退出或者
// 被移动到 CLH队列后 退出
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();
}
}
// 如果=true 那么 肯定是出现了异常 之前的那个方法
if (g.broken)
throw new BrokenBarrierException();
// 如果不等于 ok 没出现异常 也没有 改变状态
if (g != generation)
return index;
// 如果设置了超时 等于负数代表最大等待时间 减去 retrun 时的时
// 间 retrun 之前 回 去尝试获取锁 拿到则 retrun了
// 反正就是 不是被主动规定时间内唤醒 就是超时了
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}