前言
CyclicBarrier ,CountDownLatch 的 plus 版。在 CountDownLatch 的基础上,允许
- 循环使用,指定数量的线程被唤醒后,将再次进入下一轮阻塞、唤醒
- 允许指定一个 Runnable,在唤醒阻塞线程前执行
与 CountDownLatch 略有不同的是,不用显示的“倒计时”,而是在阻塞线程数达到 阈值 时直接唤醒该批线程
关于 CountDownLatch ,可阅读
JDK 版本
JDK11
CyclicBarrier
// 内部由 ReentrantLock 保证线程安全
private final ReentrantLock lock = new ReentrantLock();
// 用于阻塞的 Condition
private final Condition trip = lock.newCondition();
// 阻塞阈值
private final int parties;
// 唤醒线程前执行的任务
private final Runnable barrierCommand;
// 每个批次以 Generation 划分
private Generation generation = new Generation();
// 每个 Generation 的具体阈值
private int count;
// 内部类 Generation
private static class Generation {
Generation() {}
boolean broken;
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
// barrierAction 当然也可以为 null
public CyclicBarrier(int parties) {
this(parties, null);
}
public int getParties() {
return parties;
}
CyclicBarrier 的 属性 和 构造方法
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));
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 当前 Generation
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
// 响应中断
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
/**
* 阈值递减
* 当阈值到 0 时,执行指定任务(如果存在)
* 并开始下一轮 Generation
*/
int index = --count;
if (index == 0) {
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 否则阻塞当前线程
for (;;) {
try {
if (!timed)
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();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
// 如果超时则中止当前 Generation 并抛出 TimeoutException
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
// 开始新一轮 generation
private void nextGeneration() {
// 唤醒所有阻塞线程
trip.signalAll();
// 重置阈值
count = parties;
// 新的 generation
generation = new Generation();
}
// 中止当前 generation
private void breakBarrier() {
// 设置标识位
generation.broken = true;
// 重置阈值
count = parties;
// 唤醒所有阻塞线程
trip.signalAll();
}
调用 await
方法阻塞线程,当到达 阈值 后唤醒所有线程并开启下一轮。中断、超时 等会导致本轮 generation
中止
其他
// 当前 generation 是否中止
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
// 重置 Generation(中止后开启新一代)
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
// 当前阻塞的线程数
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
同样提供了 generation
的 重置 方法,以及 中止情况查看、阻塞线程数查看 的相关方法
demo
public class CyclicBarrierTest {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
CyclicBarrier cyclicBarrier =
new CyclicBarrier(3, () -> System.out.println("boom"));
while (true) {
new Thread(() -> {
System.out.print("di ");
try {
cyclicBarrier.await();
// 超时而导致 TimeoutException
// cyclicBarrier.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
/*} catch (TimeoutException e) {
e.printStackTrace();*/
}
System.out.print(".");
}).start();
TimeUnit.SECONDS.sleep(1);
}
}
}
结果:
di di di boom
...di di di boom
...di di di boom
...
定时炸弹
总结
很好用的一个类,可以看作是 CountDownLatch 的升级版~