关于CyclicBarrier
CyclicBarrier是JUC包中提供的多线程同步工具类,可以设置一个集合点,线程到达该集合点之后都需要阻塞,直到所有线程都到达该集合点,才能执行下一步操作,并且支持重复使用。
简单示例
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("所有线程已到达集合点...");
}
});
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "到达集合点");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
源码分析
成员变量分析
// 同步锁,保证await()方法在同一时刻只有一个线程执行
private final ReentrantLock lock = new ReentrantLock();
// 调用await()方法可以释放lock.lock()获取的锁,并使当前线程阻塞
// 调用signal()或signalAll()方法唤醒调用await方法阻塞的线程
private final Condition trip = lock.newCondition();
// 需要到达集合点的线程总数
private final int parties;
// 所有线程到达集合点之后回调接口,可以为空
private final Runnable barrierCommand;
// 标记当前CyclicBarrier的状态
private Generation generation = new Generation();
// 计数器,剩余需要到达集合点的线程数(调用await()方法,该值就会-1)
private int count;
private static class Generation {
// CyclicBarrier中断标识,线程中断、await超时,执行过程异常,都会修改该标识为true
boolean broken = false;
}
构造函数分析
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 需要到达集合点的线程数
this.parties = parties;
// 计数器,剩余到达集合点的线程数(使用该变量就是为了重复使用,当所有线程都到达集合点之后,会将该值重置为parties,后续线程再调用await()方法又可以实现阻塞,达到重复使用的效果)
this.count = parties;
// 所有线程到达集合点之后的回调接口
this.barrierCommand = barrierAction;
}
await()方法分析
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
await()方法调用有两种方式,一种永不超时,一种可以设置超时时间,底层最终都调用了dowait()方法。
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock(); // 获取锁,保证同一时刻只有一个线程进入
try {
final Generation g = generation; // 获取当前屏障的状态
if (g.broken) // 说明屏障已被破坏(线程超时、被中断),此时直接抛出异常,线程结束
throw new BrokenBarrierException();
// 线程如果被中断,结束屏障,并唤醒所有阻塞线程,最后抛出异常,线程结束
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 剩余线程数-1
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();
}
}
// 自选,阻塞,等待所有线程全部到达屏障后被其他线程唤醒
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;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
// 主要做三件事
// 1.设置屏障已经被中断,其他线程调用await()方法直接抛出异常
// 2.重置count内容,即重置屏障
// 3.唤醒所有阻塞线程
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
// 重置屏障,供下一次使用
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}