zoukankan      html  css  js  c++  java
  • 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高并发程序设计》 葛一鸣 郭超 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    UVA 10305 Ordering Tasks(拓扑排序)
    UVA 1152 4 Values whose Sum is 0(中途相遇法)
    UVA 1103 Ancient Messages
    HDU 2141 Can you find it?
    POJ 2456 Aggressive cows(二分+贪心)
    Tallest Cow【模拟】
    Tallest Cow【模拟】
    激光炸弹【模拟】
    激光炸弹【模拟】
    激光炸弹【模拟】
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/9700768.html
Copyright © 2011-2022 走看看