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 }
View Code

其中的一种运行结果为:(但是起跑一定在准备好了之后)

1 2号选手 准备好了...
2 3号选手 准备好了...
3 1号选手 准备好了...
4 1号选手 起跑!
5 2号选手 起跑!
6 3号选手 起跑!
View Code

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

posted @ 2020-12-27 16:19  Hermioner  阅读(96)  评论(0编辑  收藏  举报