title: JUC.CyclicBarrier
CyclicBarrier提纲
CyclicBarrier简介
如果说CountDownLatch让一个或者多个线程等待其它线程完成操作。
那么CyclicBarrier就是多个线程之间互相等待。
具体可以举一个例子来理解,比如说五个人组团打lol。得等到五个人都吃饱喝足了然后一起到齐了才能开始游戏,进入游戏界面得五个人都点确定才能继续开始。
这里出现了两个阶段的任务执行,如果使用CountDownLatch完全没法实现。
但是根据存在即合理的原则,CountDownLatch也有它的好处。
简单任务多个线程执行结束没有后续处理只需要一个或多个等待线程处理还是用CountDownLatch吧。毕竟不用放入阻塞队列占用线程了。该干嘛干嘛去
具体怎么用还是根据实际业务场景分析来使用没有绝对的方案。
CyclicBarrier源码解析
CyclicBarrier内部使用的是ReentrantLock加Condition实现,通过构造函数初始化parties成员个数。
下面看具体的源码比较通俗易懂。
//初始化parties线程个数
public CyclicBarrier(int parties) {
this(parties, null);
}
//初始化CyclicBarrier之后一般会在每个线程的执行函数里面调用await方法,里面具体通过dowait执行。
//dowait可以分为两个阶段。首先第一阶段都放入阻塞队列,第二阶段全部到齐唤醒所有线程。
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();//加锁因为要修改count
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped当count为0的时候代表所有线程抵达,进入唤醒阶段。
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();//重置CyclicBarrier
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 一个死循环,提前到达的线程进入然后通过lockSupport阻塞住。
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();
if (g != generation)//被最后一个线程唤醒的时候generation已近不相等了,退出死循环。
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();//释放锁让其它被阻塞的线程也执行同样的方法释放。
}
}
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();//唤醒所有线程
// set up next generation
count = parties;//重置CyclicBarrier
generation = new Generation();
}
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @ClassName CyclicBarrierTest
* @Description ToDo
* @Author Allen
* @Date 2018/12/9 16:39
* @Version
*/
public class CyclicBarrierTest {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("阶段完成");
}
});
new Thread(() -> {
try {
System.out.println("A抵达");
cyclicBarrier.await();
System.out.println("A继续到终点");
cyclicBarrier.await();
System.out.println("A结束");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("B抵达");
cyclicBarrier.await();
System.out.println("B继续到终点");
cyclicBarrier.await();
System.out.println("B结束");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 运行结果如下面所示:
*
* A抵达
* B抵达
* 阶段完成
* B继续到终点
* A继续到终点
* 阶段完成
* A结束
* B结束
*/
总结一下在需要线程等待某事件发生或者有多阶段执行的情况下使用CyclicBarrier是一个不错的选择。
但是也存在一个问题,那就是CyclicBarrier的parties线程数没法重置。
不过一般影响不大,有没有解决方案?答案当然是有的JDK的大佬们啥都替想好了。后面再说,或者自己百度一下。
欢迎扫码加入知识星球继续讨论