背景
CyclicBarrier是java.util.concurrent包下提供的另外一个常用的线程组同步工具类,顾名思义,是个可循环利用的栅栏。同样参考javadoc上的定义: “A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.” 用于一组子线程间各线程进行等待的场景。
关键方法
a) 构造方法
public CyclicBarrier(int parties); public CyclicBarrier(int parties, Runnable barrierAction);
入参是参数方的线程数量,barrierAction是指当所有线程都到达同一栅栏后,需要做的响应,如果非空则在最后一线程到达栅栏时,由最后到达的线程,进行执行。
b) await方法
public int await() throws InterruptedException, BrokenBarrierException;
由参与线程进行调用,报告自身任务已经完成或就绪。如果
一个示例
将前面的倒数10个数进行火箭发射的例子,其实也是可以用CyclicBarrier来实现的。
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; public class RocketFireCyclicBarrierImpl { private static final CyclicBarrier barrier = new CyclicBarrier(2, () -> System.out.println("点火!!")); private static final AtomicInteger counter = new AtomicInteger(10); public static void main(String[] args) { System.out.println("开始倒数"); ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2); fixedThreadPool.submit(new NumberPrinter(true, barrier)); fixedThreadPool.submit(new NumberPrinter(false, barrier)); fixedThreadPool.shutdown(); } private static class NumberPrinter implements Runnable { private final boolean evenNumberPrinter; private final CyclicBarrier cyclicBarrier; public NumberPrinter(boolean evenNumberPrinter, CyclicBarrier cyclicBarrier) { this.evenNumberPrinter = evenNumberPrinter; this.cyclicBarrier = cyclicBarrier; } @Override public void run() { while (true) { synchronized (counter) { int v = counter.get(); if (v < 1) { break; } if ((evenNumberPrinter && v % 2 == 0) || (!evenNumberPrinter && v % 2 == 1)) { System.out.println(counter.getAndDecrement()); } } } try { cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } } } }
CyclicBarrier与CountDownLatch的异同和总结
两个同步工具类在某些场景下,能实现相似的功能,也有一些使用场景上的不同。
相同之处:两者都有类似的线程组同步机制,都能实现一组线程任务结束通知的处理。
CountDownLatch | CyclicBarrier |
减计数方式 | 加计数方式 |
计算为0时释放所有等待的线程 | 计数达到指定值时释放所有等待线程 |
计数为0时,无法重置 | 计数达到指定值时,计数置为0重新开始 |
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响 | 调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞 |
不可重复利用 | 可重复利用 |
通用使用场景的总结:CountDownLatch通常是主线程任务进行拆分到子线程进行各自处理,最后由主线程进行汇总,到达的后置任务由主线程负责完成。例如生活化的场景,CountDownLatch适合跑步比赛模拟场边的裁判,在比赛开始前等所有运动员在跑道前就位后进行鸣枪开始比赛,以及在比赛结束后统计成绩。
CyclicBarrier则是完成由子线程组内部进行等待协同,由最后到达者完成到达后置任务,主线程并不参与,并且CyclicBarrier是可循环计数的。例如生活化的场景,CyclicBarrier适合模拟各种集合的场景,最晚到的人,得自罚三杯一样。