• 并发编程常用工具类(一) countDownLatch和cyclicBarrier的使用对比


    1.CountDownLatch

              countDownLatch的作用是让一组线程等待其他线程完成工作以后在执行,相当于加强版的join(不懂可以百度一下join的用法),一般在初始化的时候会在构造方法传入计数器,

    后续,在其他线程中每次调用countDown方法计数器减一,一般在需要等待的线程中调用countDownLatch的await方法阻塞线程,在当计数器为0时,等待线程继续运行。

              光看上面的定义描述不是很直观,我们再来结合代码看一下实际运用:

     1 public class UseCountDownLatch {
     2     
     3     static CountDownLatch latch = new CountDownLatch(6);
     4     //初始化线程(只有一步,有4个)
     5     private static class InitThread implements Runnable{
     6 
     7         @Override
     8         public void run() {
     9             System.out.println("Thread_"+Thread.currentThread().getId()
    10                     +" ready init work......");
    11             latch.countDown();//初始化线程完成工作了,countDown方法只扣减一次;
    12             for(int i =0;i<2;i++) {
    13                 System.out.println("Thread_"+Thread.currentThread().getId()
    14                         +" ........continue do its work");
    15             }
    16         }
    17     }
    18     //业务线程
    19     private static class BusiThread implements Runnable{
    20 
    21         @Override
    22         public void run() {
    23             try {
    24                 latch.await();
    25             } catch (InterruptedException e) {
    26                 e.printStackTrace();
    27             }
    28             for(int i =0;i<3;i++) {
    29                 System.out.println("BusiThread_"+Thread.currentThread().getId()
    30                         +" do business-----");
    31             }
    32         }
    33     }
    34 
    35     public static void main(String[] args) throws InterruptedException {
    36         //单独的初始化线程,初始化分为2步,需要扣减两次
    37         new Thread(new Runnable() {
    38             @Override
    39             public void run() {
    40                 SleepTools.ms(1);
    41                 System.out.println("Thread_"+Thread.currentThread().getId()
    42                         +" ready init work step 1st......");
    43                 latch.countDown();//每完成一步初始化工作,扣减一次
    44                 System.out.println("begin step 2nd.......");
    45                 SleepTools.ms(1);
    46                 System.out.println("Thread_"+Thread.currentThread().getId()
    47                         +" ready init work step 2nd......");
    48                 latch.countDown();//每完成一步初始化工作,扣减一次
    49             } 
    50         }).start();
    51         new Thread(new BusiThread()).start();
    52         for(int i=0;i<=3;i++){
    53             Thread thread = new Thread(new InitThread());
    54             thread.start();
    55         }
    56 
    57         latch.await();
    58         System.out.println("Main do ites work........");
    59     }
    60 }

    运行结果可以看到,两个初始化线程先跑,当两个初始化线程跑完了,latch的计数器减为0,阻塞放开,主线程和业务线程继续往下运行。但是,在设计这部分算法的时候需要注意,有可能会出现计数器没有减为0,则线程一直阻塞,导致程序卡死。

              countDownLatch一般使用在多线程并发之后需要对结果进行处理,而我们无法控制所有线程的执行时间,所以在这里加上阻塞,等到只有线程全部执行完。

    2.CyclicBarrier

              cyclicBarrier从业务角度来说,和countDownLatch比较类似(具体对比后面会专门介绍),其作用类似一个屏障(英文中也是屏障的意思),阻隔线程直到全部到达屏障点,放开屏障。一般可以用在多线程统计的时候,

              从代码角度来看,cyclicBarrier有两个构造方法,CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction);

              两个方法的区别是第一个只是记录了线程计数器的个数,而第二个不仅记录了计数器,当屏障放开时,会执行第二个参数线程的方法(第二个参数一般传入的是一个实现了Runnable的线程方法)

    再来结合代码深入了解一下:

              

     1 public class UseCyclicBarrier {
     2     
     3     private static CyclicBarrier barrier 
     4         = new CyclicBarrier(5,new CollectThread());
     5     
     6     private static ConcurrentHashMap<String,Long> resultMap
     7             = new ConcurrentHashMap<>();//存放子线程工作结果的容器
     8 
     9     public static void main(String[] args) {
    10         new Thread(){
    11             @Override
    12             public void run() {
    13                 long id = Thread.currentThread().getId();//线程本身的处理结果
    14                 resultMap.put(Thread.currentThread().getId()+"",id);
    15                 Random r = new Random();//随机决定工作线程的是否睡眠
    16                 try {
    17                     if(r.nextBoolean()) {
    18                         Thread.sleep(2000+id);
    19                         System.out.println("Thread_"+id+" ....do something ");
    20                     }
    21                     System.out.println(id+"....is await");
    22                     barrier.await();
    23                     Thread.sleep(1000+id);
    24                     System.out.println("Thread_"+id+" ....do its business ");
    25                 } catch (Exception e) {
    26                     e.printStackTrace();
    27                 }
    28             }
    29         }.start();
    30         for(int i=0;i<=3;i++){
    31             Thread thread = new Thread(new SubThread());
    32             thread.start();
    33         }
    34     }
    35 
    36     //负责屏障开放以后的工作
    37     private static class CollectThread implements Runnable{
    38 
    39         @Override
    40         public void run() {
    41             StringBuilder result = new StringBuilder();
    42             for(Map.Entry<String,Long> workResult:resultMap.entrySet()){
    43                 result.append("["+workResult.getValue()+"]");
    44             }
    45             System.out.println(" the result = "+ result);
    46             System.out.println("do other business........");
    47         }
    48     }
    49 
    50     //工作线程
    51     private static class SubThread implements Runnable{
    52 
    53         @Override
    54         public void run() {
    55             long id = Thread.currentThread().getId();//线程本身的处理结果
    56             resultMap.put(Thread.currentThread().getId()+"",id);
    57             Random r = new Random();//随机决定工作线程的是否睡眠
    58             try {
    59                 if(r.nextBoolean()) {
    60                     Thread.sleep(2000+id);
    61                     System.out.println("Thread_"+id+" ....do something ");
    62                 }
    63                 System.out.println(id+"....is await");
    64                 barrier.await();
    65                 Thread.sleep(1000+id);
    66                 System.out.println("Thread_"+id+" ....do its business ");
    67             } catch (Exception e) {
    68                 e.printStackTrace();
    69             }
    70         }
    71     }
    72 }

       输出结果:       11....is await
                                12....is await
                                 Thread_10 ....do something
                         10....is await
    Thread_13 ....do something
    13....is await
    Thread_14 ....do something
    14....is await
     the result = [11][12][13][14][10]
    do other business........
    Thread_10 ....do its business
    Thread_11 ....do its business
    Thread_12 ....do its business
    Thread_13 ....do its business
    Thread_14 ....do its business

    3.countDownLatch和cyclicBarrier区别

            (1)countDownlatch的计数器由调用countDown方法次数决定,每次调用计数器减一,可以在一个线程中调用多次,而cyclicBarrier计数器取决调用await方法的线程个数。

              (2)  countDownLatch只能用一次,而cyclicBarrier可以循环使用。并且cyclicBarrier可以调用reset方法重置计数器,可以在线程故障重新启用线程调用。

              (3)  二者在内部方法也有很多区别,具体有兴趣的可以去看看源码。

     参考文章:http://ifeve.com/concurrency-cyclicbarrier/

              

  • 相关阅读:
    国外程序员整理的 C++ 资源大全
    31部黑客电影
    向windows添加环境变量
    windows 查看动态连接库和静态连接库的方法
    十大最值得注意的MySQL变量
    源码圈 300 胖友的书单整理
    82岁“极客”老人用云计算写族谱, 90后败给“30”后!
    Redis 实现队列http://igeekbar.com/igeekbar/post/436.htm
    借助CSS Shapes实现元素滚动自动环绕iPhone X的刘海
    听说程序猿的密码大多是这样滴~看完心累中。。。
  • 原文地址:https://www.cnblogs.com/gmt-hao/p/9476063.html
Copyright © 2020-2023  润新知