1 CyclicBarrier的应用
CyclicBarrier可以理解为循环栅栏。
在使用中要求必须规定数量的线程都到达栅栏后,所有在栅栏前等待的线程才能都继续执行。
CyclicBarrier可以循环使用,在本次执行完毕后会将计数器归位。
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
// 声明栅栏
CyclicBarrier barrier = new CyclicBarrier(3,() -> {
System.out.println("乘客上车了");
});
new Thread(() -> {
System.out.println("第一位乘客就位");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
System.out.println("第二位乘客就位");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
barrier.await();
System.out.println("发车");
}
2 CyclicBarrier源码解析
2.1 有参构造
CyclicBarrier没有直接使用AQS,而是使用ReentrantLock,底层依旧是AQS提供的支持
public CyclicBarrier(int parties, Runnable barrierAction) {、
// 健壮性判断!
if (parties <= 0) throw new IllegalArgumentException();
// parties是final修饰的,需要在重置时,使用!
this.parties = parties;
// count是在执行await用来计数的。
this.count = parties;
// 当计数count为0时 ,执行这个Runnnable!再唤醒被阻塞的线程(只在每次count到0时执行一次)
this.barrierCommand = barrierAction;
}
2.2 await
await方法内无逻辑实现,实际调用的是dowait(boolean timed, long nanos),调用时可以穿入超时时间 如果等待超时,将会抛出TimeoutException,修改CyclicBarrier中的generation,将broken设置为true,在重置之前将无法使用。
不存在超时的情况下: 线程执行await方法,会对count-1,再判断count是否为0
- 如果不为0,需要添加到AQS中的ConditionObject的Waiter队列中排队,并park当前线程
- 如果为0,证明线程到齐,需要执行nextGeneration,会先将Waiter队列中的Node全部转移到AQS的队列中,并且有后继节点的,ws设置为-1。没有后继节点设置为0。然后重置count和broker标记。等到unlock执行后,每个线程都会被唤醒。
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
// CyclicBarrier是基于ReentrantLock-Condition的await和singalAll方法实现的。
// 相当于synchronized中使用wait和notify
// 挂起,会释放锁资源。
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 里面就是boolean,默认false
final Generation g = generation;
// 判断之前栅栏加入线程时,是否有超时、中断等问题,如果有,设置boolean为true,其他线程再进来,直接抛出BrokenBarrierException
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 对计数器count--
int index = --count;
// 如果--完,是0,代表突破栅栏
if (index == 0) {
// 默认false
boolean ranAction = false;
try {
// 如果你用的是2个参数的有参构造,说明你传入了任务,index == 0,先执行CyclicBarrier有参的任务
final Runnable command = barrierCommand;
if (command != null)
command.run();
// 设置为true
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// --完之后,index不是0,代表还需要等待其他线程
for (;;) {
try {
// 如果没设置超时时间。 await()
if (!timed)
trip.await();
// 设置了超时时间。 await(1,SECOND)
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();
}
}
// count到0,唤醒所有队列里的线程线程
private void nextGeneration() {
// 这个方法就是将Waiter队列中的节点遍历都扔到AQS的队列中,真正唤醒的时机,是unlock方法
trip.signalAll();
// 重置计数器
count = parties;
// 重置异常判断
generation = new Generation();
}