CountDownLatch(倒计时器)、CyclicBarrier(循环栅栏)

CountDownLatch():

CountDownLatch是一个非常实用的多线程控制工具类,这个工具通常用来控制线程等待,它可以让某一个线程等到倒计时结束,再开始执行。

CountDownLatch的构造函数接收一个整数为参数,即当前这个计数器的计数个数。

public CountDownLatch(int count)

下面演示下CountDownLatch的使用:

 1 public class CountDownLatchDemo implements Runnable {
 2 
 3     static CountDownLatch count = new CountDownLatch(10);
 4     static CountDownLatchDemo demo = new CountDownLatchDemo();
 5 
 6     @Override
 7     public void run() {
 8         try {
 9             //模拟检查任务
10             Thread.sleep(new Random().nextInt(10) * 1000);
11             System.out.println("check complete!");
12             count.countDown();
13         } catch (InterruptedException e) {
14             e.printStackTrace();
15         }
16     }
17     //测试
18     public static void main(String[] args) throws InterruptedException {
19         ExecutorService exec = Executors.newFixedThreadPool(10);
20         for (int i = 0;i < 10;i++){
21             exec.submit(demo);
22         }
23         //等待完成所有线程检查
24         System.out.println("开始等待:" + Thread.currentThread().getName());
25         count.await();
26         //检查完成
27         System.out.println("检查完成: " + Thread.currentThread().getName());
28         exec.shutdown();
29     }
30 }

输出结果:

开始等待:main
check complete!
check complete!
check complete!
check complete!
check complete!
check complete!
check complete!
check complete!
check complete!
check complete!
检查完成: main

 上述代码的第3行,生成一个CountDownLatch实例,计数数量为10,表示需要有10个线程完成任务,等待在CountDownLatch上的线程才能继续执行。代码第12行,使用了CountDownLatch.countDown()方法,也就是通知CountDownLatch,一个线程已经完成任务,倒计时可以减一了。在第25行,使用了CountDownLatch.await()方法,要求主线程等待所有10个任务完成后,主线程才能继续执行。这点从输出结果也可以看出,主线程一直等到10个线程完成后,才继续执行。

 CyclicBarrier():

CyclicBarrier是另外一种多线程并发控制实用工具。和CountDownLatch非常类似,它也可以实现多线程间的计数等待,但它的功能比CountDownLatch更加复杂和强大。比如:我们把计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程,这就是循环栅栏的含义。

CyclicBarrier可以接收一个参数做为barrierAction。这个barrierAction就是当计数器完成一次计数后,系统会执行的动作。构造函数如下,其中parties表示计数总数,也就是线程的参与总数。

public CyclicBarrier(int parties,Runnable barrierAction)

下面实例演示:

 1 public class CyclicBarrierDemo {
 2 
 3     public static class Soldier implements Runnable{
 4 
 5         private String soldier;
 6         private final CyclicBarrier cyclicBarrier;
 7 
 8         public Soldier(String soldier,CyclicBarrier cyclicBarrier){
 9             this.soldier = soldier;
10             this.cyclicBarrier = cyclicBarrier;
11         }
12 
13         @Override
14         public void run() {
15             try{
16                 //等待所有士兵到齐
17                 cyclicBarrier.await();
18                 doWork();
19                 //等待所有士兵完成任务
20                 cyclicBarrier.await();
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             } catch (BrokenBarrierException e) {
24                 e.printStackTrace();
25             }
26         }
27         //任务
28         void doWork(){
29             try {
30                 Thread.sleep(Math.abs(new Random().nextInt() % 10000));
31             } catch (InterruptedException e) {
32                 e.printStackTrace();
33             }finally {
34                 System.out.println(soldier + "完成任务!");
35             }
36         }
37         //等待完成后,做的事
38         public static class BarrierRun implements Runnable{
39 
40             boolean flag;
41             int N;
42 
43             public BarrierRun(boolean flag,int N){
44                 this.flag = flag;
45                 this.N = N;
46             }
47 
48             @Override
49             public void run() {
50                 if (flag){
51                     System.out.println("司令:士兵 " + N + "个,任务完成!");
52                 }else {
53                     System.out.println("司令:士兵 " + N + "个,集合完毕!");
54                     flag = true;
55                 }
56             }
57         }
58         //测试
59         public static void main(String[] args){
60             final int N = 10;
61             Thread[] allSoldier = new Thread[N];
62             boolean flag = false;
63             CyclicBarrier cyclicBarrier = new CyclicBarrier(N,new BarrierRun(flag,N));
64             //设置屏障点, 主要是为了执行这个方法
65             System.out.println("集合队伍!");
66             for (int i = 0;i < N;i++){
67                 System.out.println("士兵"+ i + "报道!");
68                 allSoldier[i] = new Thread(new Soldier("士兵"+ i,cyclicBarrier));
69                 allSoldier[i].start();
70             }
71         }
72     }
73 }

 上述代码的第63行,创建了CyclicBarrier实例,并将计数器设置为10,并要求在计数器达到指标后,执行49行run()方法。在第17行,每一个士兵线程都会等待,直到所有的士兵都集合完毕。集合完毕后,意味着CyclicBarrier的一次计数完成,当再一次调用CyclicBarrier.await()时,会进行下一次的计数。第18行,模拟了士兵的任务,当一个士兵完成任务后,代码的第19行就会让CyclicBarrier开始下一次的计数,这次计数的目的就是监控所有士兵是否全部完成了任务。一旦完成,49行的run()方法又会被调用,打印相应的信息。上述代码执行后,输出结果如下:

集合队伍!
士兵0报道!
士兵1报道!
士兵2报道!
士兵3报道!
士兵4报道!
士兵5报道!
士兵6报道!
士兵7报道!
士兵8报道!
士兵9报道!
司令:士兵 10个,集合完毕!
士兵1完成任务!
士兵7完成任务!
士兵9完成任务!
士兵0完成任务!
士兵3完成任务!
士兵4完成任务!
士兵6完成任务!
士兵2完成任务!
士兵5完成任务!
士兵8完成任务!
司令:士兵 10个,任务完成! 

可以看出:整个的工作过程是这样的,先开始士兵集合,集合完毕后,打印集合完毕通知;开始任务,进行下一次的计数,所有任务完成后,打印任务完成通知。 

注意:

  CyclicBarrier.await()方法可能会抛出两个异常。一个是 InterruptedException和 BrokenBarrierException ,InterruptedException表示线程在等待过程中,线程被中断,大部分线程等待的方法,都可能会抛出这个异常;BrokenBarrierException则表示当前的CyclicBarrier已经破损了,可能系统已经没有办法等待所有的线程到齐了,如果继续等待,可能就是徒劳无功的,因此,在这个CyclicBarrier上等待的其他线程,都会不再等待,以反常的方式离开。

CountDownLatch和CyclicBarrier的比较

  ❤ CountDownLatch:是一个(或者多个)线程,等待其他N个线程完成某件事情后才能执行;CyclicBarrier:N个线程互相等待,任何一个线程完成前,所有线程都必须等待。

  ❤ CountDownLatch:一次性的;CyclicBarrier:循环使用的,可以重复的;

  ❤ CountDownLatch基于AQS;CyclicBarrier基于锁和Condition;本质上都是依赖于volatile和CAS实现的。

 

 参考:《Java高并发程序设计》 葛一鸣 郭超 编著:

posted on 2018-09-25 17:34  AoTuDeMan  阅读(489)  评论(0编辑  收藏  举报

导航