CyclicBarrier顾名思义回环栅栏,是Java并发库中的一个类。栅栏的意思是可以把多个线程拦住,先到的线程必须等待后面的线程全都到达,然后所有线程同时往下走。回环的意思是,每次拦截之后,如果你还想拦截下一批线程,无需手动初始化就可以直接使用。
例如有几个朋友约定一起到一个饭店吃饭,必须所有人都到达之后,由最后一个人点菜,然后开始吃。
public class CyclicBarrieTest {
// 自定义工作线程
private static class Friend extends Thread {
private CyclicBarrier cyclicBarrier;
public Friend(CyclicBarrier cyclicBarrier, String name) {
super(name);
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
super.run();
try {
System.out.println(Thread.currentThread().getName() + "开始等待其他朋友");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "开始吃饭");
// 朋友开始处理,这里用Thread.sleep()来模拟业务处理
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "吃饭完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount, () -> {
System.out.println("点菜");
});
for (int i = 0; i < threadCount; i++) {
System.out.println("约好朋友" + i);
Friend worker = new Friend(cyclicBarrier, "朋友" + i);
worker.start();
}
}
}
运行之:
约好朋友0
约好朋友1
约好朋友2
朋友1开始等待其他朋友
朋友0开始等待其他朋友
朋友2开始等待其他朋友
点菜
朋友2开始吃饭
朋友1开始吃饭
朋友0开始吃饭
朋友1吃饭完毕
朋友0吃饭完毕
朋友2吃饭完毕
可以看出先是朋友之间约好,然后三个朋友依次到达饭店,先到的必须等到最后一个到达的,最后一个到达之后点菜。点完之后三个朋友同时开始吃,但有的吃的快,有的慢,最后都吃完,程序也就运行结束了。
下面分析一下关键的代码,首先CyclicBarrie初始化时,可以传入一个整型变量parties,以及一个Runnable变量barrieAction(可选)。parties代表可以有几个线程等待,barrieAction是可选的,代表最后一个线程到达之后,在最后一个线程中运行barrieAction。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
这里还有一个count,count代表当前还有几个线程在等待。
await方法代表执行线程已经到达,开始等待其他线程。
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
底层调用了doAwait方法
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
// 执行所有线程到达之后的指定任务
Runnable command = barrierCommand;
if (command != null) {
try {
command.run();
} catch (Throwable ex) {
breakBarrier();
throw ex;
}
}
// 唤醒所有等待线程执行,同时重新初始化栅栏
nextGeneration();
return 0;
}
// loop until tripped, broken, interrupted, or timed out
// 不是最后一个到达的线程,则循环等待打破循环
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 {
// 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();
// 如果已经被其他线程重新初始化,则返回index
if (g != generation)
return index;
// 如果超过等待时间,则打破栅栏,并抛异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
再看看打破栅栏:
/**
* Sets current barrier generation as broken and wakes up everyone.
* Called only while holding lock.
* 设置栅栏为已打破状态,将等待线程恢复为parties,同时通知所有等待线程恢复执行
*/
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
再看看nextGeneration()方法:
// 通知所有等待线程恢复执行,将等待线程数恢复为parties,初始化一个新的Generation,代表开启一个新的等待栅栏
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
以上就是主要的代码分析,其实还是比较简单的。仔细想想就能看明白