Java多线程之CyclicBarrier

背景

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适合模拟各种集合的场景,最晚到的人,得自罚三杯一样。

 

上一篇:Java中使用CyclicBarrier


下一篇:并发工具-CyclicBarrier源码简析