CountDownLatch
和CyclicBarrier
是jdk concurrent包下非常有用的两个并发工具类,它们提供了一种控制并发流程的手段。
CyclicBarrier
CyclicBarrier
,让一组线程到达一个同步点后再一起继续运行,其中任意一个线程未达到同步点,其他到达的线程均会被阻塞。
- CountDownLatch : 一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
- CyclicBarrier: 多个线程互相等待,直到到达同一个同步点,再继续一起执行。
对于CountDownLatch
来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。
案例
假设有只有的一个场景:每个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只要有一个人没有准备好,大家都等待.
Runner.java
|
|
CyclicBarrierTest.java
|
|
CyclicBarrier
源码分析
- 构造方法
CyclicBarrier
提供两个构造方法CyclicBarrier(int parties)
和CyclicBarrier(int parties, Runnable barrierAction)
:
|
|
默认构造方法,参数表示拦截的线程数量。
|
|
由于线程之前的调度是由CPU决定的,所以默认的构造方法无法设置线程执行优先级,CyclicBarrier提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction)
,用于在线程到达同步点时,优先执行线程barrierAction,这样可以更加方便的处理一些负责的业务场景。
await
方法
创建CyclicBarrier
后,每个线程调用await
方法告诉CyclicBarrier
自己已经到达同步点,然后当前线程被阻塞。接下来我们来看看await
方法的具体实现。
|
|
CyclicBarrier
同样提供带超时时间的await和不带超时时间的await
dowait
方法
- 在
dowait
的前段部分,主要完成了当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction
决定优先执行的线程。 - 在
dowait
的实现后半部分,主要实现了线程未到达同步点(barrier)时,线程进入Condition
自旋等待,直到等待超时或者所有线程都到达barrier时被唤醒。
|
|
在整个dowait:
- 使用ReentrantLock保证每一次操作线程安全;
- 线程等待/唤醒使用Lock配合Condition来实现;
- 线程被唤醒的条件:等待超时或者所有线程都到达barrier。
总结:
CyclicBarrier
就是一个栅栏,等待所有线程到达后再执行相关的操作。CyclicBarrier
在释放等待线程后可以重用。