Concurrent同步工具类02 - CyclicBarrier
简介
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
CyclicBarrier一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。
CyclicBarrier的应用场景
CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。
应用举例:
1 package com.test.lesson01; 2 3 import java.io.IOException; 4 import java.util.Random; 5 import java.util.concurrent.BrokenBarrierException; 6 import java.util.concurrent.CyclicBarrier; 7 import java.util.concurrent.ExecutorService; 8 import java.util.concurrent.Executors; 9 10 public class CyclicBarrierTest1 { 11 12 public static void main(String[] args) throws IOException, InterruptedException { 13 //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去 14 CyclicBarrier barrier = new CyclicBarrier(3); 15 16 ExecutorService executor = Executors.newFixedThreadPool(3); 17 executor.submit(new Thread(new Runner(barrier, "1号选手"))); 18 executor.submit(new Thread(new Runner(barrier, "2号选手"))); 19 executor.submit(new Thread(new Runner(barrier, "3号选手"))); 20 21 executor.shutdown(); 22 } 23 } 24 25 class Runner implements Runnable { 26 // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 27 private CyclicBarrier barrier; 28 29 private String name; 30 31 public Runner(CyclicBarrier barrier, String name) { 32 super(); 33 this.barrier = barrier; 34 this.name = name; 35 } 36 37 @Override 38 public void run() { 39 try { 40 Thread.sleep(1000 * (new Random()).nextInt(8)); 41 System.out.println(name + " 准备好了..."); 42 // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 43 barrier.await(); 44 //设置等待时间,如果等待了1秒,最后一个线程还没有就位,则自己继续运行,但是会导致Barrier被标记为一个已经破坏的Barrier 45 //barrier.await(1,TimeUnit.SECONDS); 46 } catch (InterruptedException e) { 47 System.out.println(name + " 中断异常!"); 48 } catch (BrokenBarrierException e) { 49 System.out.println(name + " Barrier损坏异常!"); 50 } 51 System.out.println(name + " 起跑!"); 52 } 53 }
其中的一种运行结果为:(但是起跑一定在准备好了之后)
1 2号选手 准备好了... 2 3号选手 准备好了... 3 1号选手 准备好了... 4 1号选手 起跑! 5 2号选手 起跑! 6 3号选手 起跑!
CyclicBarrier与CountDownLatch的区别
至此我们难免会将CyclicBarrier与CountDownLatch进行一番比较。这两个类都可以实现一组线程在到达某个条件之前进行等待,它们内部都有一个计数器,当计数器的值不断的减为0的时候所有阻塞的线程将会被唤醒。
有区别的是CyclicBarrier的计数器由自己控制,而CountDownLatch的计数器则由使用者来控制,在CyclicBarrier中线程调用await方法不仅会将自己阻塞还会将计数器减1,而在CountDownLatch中线程调用await方法只是将自己阻塞而不会减少计数器的值。
另外,CountDownLatch只能拦截一轮,而CyclicBarrier可以实现循环拦截。一般来说用CyclicBarrier可以实现CountDownLatch的功能,而反之则不能,例如上面的赛马程序就只能使用CyclicBarrier来实现。
参考文献:
http://ifeve.com/concurrency-cyclicbarrier/#more-14746
https://blog.csdn.net/qq_39241239/article/details/87030142