zoukankan      html  css  js  c++  java
  • 倒计时器CountDownLatch与同步屏障CyclicBarrier

    CountDownLatch

    CountDownLatch是一个非常实用的多线程控制工具类,这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。在这里指CountDownLatch.await()方法在倒计数为0之前会阻塞程序运行。

    CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作。例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作。这些准备工作彼此独立,所以可以并发执行以提高速度。在这个场景下就可以使用 CountDownLatch 协调线程之间的调度了。

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

    下面我们看一个经典案例: 

    public class CountDownLatchDemo implements Runnable{
        static final CountDownLatch countDownLatch = new CountDownLatch(10);
        @Override public void run() {
            try {
                Thread.sleep(new Random().nextInt(10)*1000);
                System.out.println("Check complete!");
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final CountDownLatchDemo demo = new CountDownLatchDemo();
            ExecutorService exec = Executors.newFixedThreadPool(10);
            for (int i = 0; i < 10; i++) {
                exec.submit(demo);
            }
            countDownLatch.await();
            System.out.println("发射火箭...Fire");
            exec.shutdown();
        }
    }

    如代码所示:主线程一直在等待线程执行完反馈结果,直到我们定义的10个线程均执行成功且调用了countDown()方法,每次调用计数器减1,到0时会立即调用主函数"发射火箭…Fire"假设我们只循环9次,那么主线程就会一直等待。

    CyclicBarrier  

    CyclicBarrier是另外一种多线程并发控制实用工具。和CountDownLatch非常类似,它也可是实现线程间的计数等待。它主要的方法就是一个:await()。await() 方法每被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。在这之后,如果再次调用 await() 方法,计数就又会变成 N-1,新一轮重新开始,这便是 Cyclic 的含义所在。

    CyclicBarrier 的使用并不难,但需要主要它所相关的异常。除了常见的异常,CyclicBarrier.await() 方法会抛出一个独有的 BrokenBarrierException。这个异常发生在当某个线程在等待本 CyclicBarrier 时被中断或超时或被重置时,其它同样在这个 CyclicBarrier 上等待的线程便会受到 BrokenBarrierException。意思就是说,同志们,别等了,有个小伙伴已经挂了,咱们如果继续等有可能会一直等下去,所有各回各家吧。

    案例:

    public class CyclicBarrierDemo {
        static class Runner implements Runnable {
            private CyclicBarrier barrier;
            private String name;
    
            public Runner(CyclicBarrier barrier, String name) {
                this.barrier = barrier;
                this.name = name;
            }
            @Override
            public void run() {
                try {
                    Thread.sleep(1000 * (new Random()).nextInt(5));
                    System.out.println(name + " 准备OK.");
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(name + " Go!!");
            }
        }
    
        public static void main(String[] args) throws IOException, InterruptedException {
            CyclicBarrier barrier = new CyclicBarrier(3);  // 3
            ExecutorService executor = Executors.newFixedThreadPool(3);
    
            executor.submit(new Thread(new Runner(barrier, "zhangsan")));
            executor.submit(new Thread(new Runner(barrier, "lisi")));
            executor.submit(new Thread(new Runner(barrier, "wangwu")));
    
            executor.shutdown();
        }
    }

    运动员准备完成以后需要等待其他运动员准备完成,所有的运动员准备完成后再一起执行System.out.println(name + " Go!!");方法。

    CyclicBarrier 和 CountDownLatch 在用法上的不同

    CountDownLatch 适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景;CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,例如同时开始一个工作。另外,CyclicBarrier 的循环特性和构造函数所接受的 Runnable 参数也是 CountDownLatch 所不具备的。

    具体点:

     

    CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。  

    CyclicBarrier       : N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

    这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。 

     

    CountDownLatch 是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的.

     

    而CyclicBarrier更像一个水闸, 线程执行就像水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流.

  • 相关阅读:
    VM安装CentOs7虚拟机后无法上网之解决方法
    vue中touchEnd事件和touchStart、touchMove获取坐标不一样,IOS绑定touch事件失效
    【个推独家】让你一次性掌握Neo4j性能优化秘籍的三大狠招
    【深度干货】异构数据的SQL一站式解决方案
    个推基于Jenkins的自动打包构建实践
    个推CTO叶新江专访| 数据智能的未来,是不提大数据但其无所不在的时代
    应用系统间数据传输方式总结(附相关概念解释)
    JavaScript中“&&”和“||”操作符的意义,深入理解和使用场景
    静态布局、自适应布局、流式布局、响应式布局、弹性布局等的概念和区别
    前端跨域问题相关知识详解(原生js和jquery两种方法实现jsonp跨域)
  • 原文地址:https://www.cnblogs.com/jianzh5/p/6124425.html
Copyright © 2011-2022 走看看