Java并发工具类需要依赖对于Java锁机制的理解,尤其是AQS,可以参见前文:
Java锁机制浅析(一)
Java锁机制浅析(二)之AQS
Java并发工具类 CountDownLatch
3.2 CyclicBarrier 线程屏障
允许一组线程全部等待彼此达到共同屏障点的同步辅助类。 它可以在等待的线程被释放之后重新使用。想象一下导弹部队饱和攻击哈哈,~所有导弹发射车到达指定位置后,开始齐射导弹。。
3.2.1主要API
方法 | 说明 |
---|---|
CyclicBarrier(int parties) | 创建一个指定数量的同步屏障对象。 |
CyclicBarrier(int parties,Runnable action) | 创建一个指定数量的同步屏障对象,当屏障破碎时执行指定的Runnable任务。 |
int await() | 阻塞调用线程直到所有线程到达屏障。 |
int await(long timeout,TimeUnit unit) | 阻塞调用线程直到所有线程到达屏障或者超时。 |
boolean isBroken() | 查询屏障是否处于破碎状态。 |
void reset() | 将屏障重置为初始状态。 |
int getNumberWaiting() | 获取正在等待屏障破碎的线程数量。 |
3.2.2 Demo
CyclicBarrier和CountDownLatch比较类似,都可以用在一些 条件等待–>条件达成–>唤醒等待 场景中。但是二者区别也比较明显:
- CyclicBarrier 是通过正向计数的方式让一方的多个线程在同一个障碍点等待彼此,当最后一个线程调用await()时,屏障破碎,所有等待的线程自动激活。通过主动调用屏障的reset()方法重置后,可以重复使用。
- CountDownLatch则是通过递减计数方式让一方的线调用await()函数进行等待,再由另外一方的线程通过countDown()逐步让计数归零后激活等待一方的线程。并且是一次性的,无法重复使用。
3.2.2.1 模拟导弹齐射
public class CyclicBarrierDemo {
public static void main(String[] args)throws Exception{
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N,()->{
System.out.println("所有导弹车到达预定地点,导弹齐射开始!");
});
for (int i = 1; i <= N; i++) {
MissileLauncher er = new MissileLauncher(i,barrier);
Thread thread = new Thread(er);
thread.start();
}
}
//模拟发射车
static class MissileLauncher implements Runnable {
private int no ;
private CyclicBarrier barrier;
MissileLauncher(int no,CyclicBarrier barrier) {
this.no = no;
this.barrier = barrier;
}
public void run() {
try {
long l = ThreadLocalRandom.current().nextLong(8000);
Thread.sleep(l); //模拟不同时间到达
System.out.println(String.format("发射车[%d]号,%s 到达指定地点!",no, LocalTime.now()));
barrier.await();
System.out.println(String.format("编号%d发射车发射!",no));
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
导弹车开始集结!
发射车[4]号,17:08:15.102 到达指定地点!
发射车[1]号,17:08:17.038 到达指定地点!
发射车[3]号,17:08:17.738 到达指定地点!
发射车[2]号,17:08:18.021 到达指定地点!
所有导弹车到达预定地点,导弹齐射开始!
编号2发射车发射!
编号4发射车发射!
编号1发射车发射!
编号3发射车发射!
3.2.2.1 模拟多轮次齐射
public class MultiRoundMissileSalvo {
public static void main(String[] args)throws Exception{
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N,()->{
System.out.println("所有导弹车到达预定地点,导弹齐射开始!");
});
for (int round = 1; round <= 3; round++) {
System.out.println(String.format("第【%d】轮导弹发射:",round));
CountDownLatch cdt = new CountDownLatch(N);
for (int i = 0; i < N; i++) {
MissileLauncher er = new MissileLauncher(i,barrier,cdt);
Thread thread = new Thread(er);
thread.start();
}
cdt.await();
barrier.reset();
}
}
//模拟发射车
static class MissileLauncher implements Runnable {
private int no ;
private CyclicBarrier barrier;
private CountDownLatch cdt;
MissileLauncher(int no,CyclicBarrier barrier ,CountDownLatch cdt) {
this.no = no;
this.barrier = barrier;
this.cdt = cdt;
}
public void run() {
try {
long l = ThreadLocalRandom.current().nextLong(8000);
Thread.sleep(l); //模拟不同时间到达
System.out.println(String.format("%s: 发射车[%d]号-到达指定地点!", LocalTime.now(),no));
barrier.await();
System.out.println(String.format("%s: 发射车[%d]号-发射!",LocalTime.now(),no));
this.cdt.countDown();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
3.2.3 源码
await()
和 await(long timeout, TimeUnit unit)
都是调用dowait(boolean timed, long nanos)
方法实现。
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/**
* 如果加上当前线程到达屏障指定数量,
* 1:由当前线程执行指定的BarrierAction
* 2:唤醒在Condition队列阻塞的其他线程并重置屏障
* 反之,则将通过内部持有的Condition队列阻塞当前线程。
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
//broken中断支持,在最后一个线程到达之前都可通过broken方法进行中断
if (g.broken)
throw new BrokenBarrierException();
//支持线程interrupted中断
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//最后的线程到达屏障后执行action,通过。nextGeneration()唤醒其他线程并重置屏障
//在finally块检测action是否正常执行完成,如果action执行异常未能完成,则中断屏障
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//唤醒阻塞线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//线程数未到达屏障指定count时
for (;;) {
try {
//通过Condition的await方法阻塞当前线程,同时将线程加入等待队列。
if (!timed)
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();
}
}
//阻塞线程被唤醒后首先检查屏障是否中断。
if (g.broken)
throw new BrokenBarrierException();
//检查是否被重置
if (g != generation)
return index;
//检查是否超时
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
//唤醒在Condition上阻塞的线程并重置屏障。
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}