zoukankan      html  css  js  c++  java
  • CountDownLatch和CyclicBarrier

    CountDownLatch类

      CountDownLatch 类在创建实例的时候,需要在构造函数中传入倒数次数,然后由需要等待的线程去调用 await 方法开始等待,而每一次其他线程调用了 countDown 方法之后,计数便会减 1,直到减为 0 时,之前等待的线程便会继续运行。

    构造方法:

      CountDownLatch(int count)

    构造一个以给定计数 CountDownLatch CountDownLatch。

    方法:

    • await() 导致当前线程等到锁存器计数到零,除非线程是 interrupted 。
    • await(long timeout, TimeUnit unit) 使当前线程等待直到锁存器计数到零为止,除非线程为 interrupted或指定的等待时间过去。
    • countDown() 减少锁存器的计数,如果计数达到零,释放所有等待的线程。
    • getCount() 返回当前计数。

    测试方法:

        private static void test5() {
            CountDownLatch latch = new CountDownLatch(3);
            ExecutorService service = Executors.newFixedThreadPool(4);
            service.submit(() -> {
                log.debug("begin...");
                sleep(1);
                latch.countDown();
                log.debug("end...{}", latch.getCount());
            });
            service.submit(() -> {
                log.debug("begin...");
                sleep(1.5);
                latch.countDown();
                log.debug("end...{}", latch.getCount());
            });
            service.submit(() -> {
                log.debug("begin...");
                sleep(2);
                latch.countDown();
                log.debug("end...{}", latch.getCount());
            });
            service.submit(()->{
                try {
                    log.debug("waiting...");
                    latch.await();
                    log.debug("wait end...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    

    image-20201231152416081

    启动和结束相隔了2秒。

    模拟加载的进程:

     private static void test2() throws InterruptedException {
            AtomicInteger num = new AtomicInteger(0);
            ExecutorService service = Executors.newFixedThreadPool(10, (r) -> {
                return new Thread(r, "t" + num.getAndIncrement());
            });
            CountDownLatch latch = new CountDownLatch(10);
            String[] all = new String[10];
            Random r = new Random();
            for (int j = 0; j < 10; j++) {
                int x = j;
                service.submit(() -> {
                    for (int i = 0; i <= 100; i++) {
                        try {
                            Thread.sleep(r.nextInt(100));
                        } catch (InterruptedException e) {
                        }
                        all[x] = Thread.currentThread().getName() + "(" + (i + "%") + ")";
                        System.out.print("
    " + Arrays.toString(all));
                    }
                    latch.countDown();
                });
            }
            latch.await();
            System.out.println("
    游戏开始...");
            service.shutdown();
        }
    

    ![GIF 2020-12-31 15-26-47](https://typora-oss.oss-cn-beijing.aliyuncs.com/GIF 2020-12-31 15-26-47.gif)

    CyclicBarrier

    CyclicBarrier 和 CountDownLatch 确实有一定的相似性,它们都能阻塞一个或者一组线程,直到某种预定的条件达到之后,这些之前在等待的线程才会统一出发,继续向下执行。正因为它们有这个相似点,你可能会认为它们的作用是完全一样的,其实并不是。
    CyclicBarrier 可以构造出一个集结点,当某一个线程执行 await() 的时候,它就会到这个集结点开始等待,等待这个栅栏被撤销。直到预定数量的线程都到了这个集结点之后,这个栅栏就会被撤销,之前等待的线程就在此刻统一出发,继续去执行剩下的任务。

      实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。

    构造方法 

    CyclicBarrier(int parties)
    创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时不执行预定义的动作。
    CyclicBarrier(int parties, Runnable barrierAction)
    创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。

    方法

    • int await() 等待所有 parties已经在这个障碍上调用了 await 。
    • int await(long timeout, TimeUnit unit) 等待所有 parties已经在此屏障上调用 await ,或指定的等待时间过去。
    • int getNumberWaiting() 返回目前正在等待障碍的各方的数量。
    • int getParties() 返回旅行这个障碍所需的parties数量。
    • boolean isBroken() 查询这个障碍是否处于破碎状态。
    • void reset() 将屏障重置为初始状态
    /**
     * @author WGR
     * @create 2020/12/31 -- 15:29
     */
    @Slf4j(topic = "c.TestCyclicBarrier")
    public class TestCyclicBarrier {
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(2);
            CyclicBarrier barrier = new CyclicBarrier(2, ()-> {
                log.debug("task1, task2 finish...");
            });
            for (int i = 0; i < 3; i++) { // task1  task2  task1
                service.submit(() -> {
                    log.debug("task1 begin...");
                    sleep(1);
                    try {
                        barrier.await(); // 2-1=1
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                });
                service.submit(() -> {
                    log.debug("task2 begin...");
                    sleep(2);
                    try {
                        barrier.await(); // 1-1=0
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                });
            }
            service.shutdown();
    
        }
    
        private static void test1() {
            ExecutorService service = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 3; i++) {
                CountDownLatch latch = new CountDownLatch(2);
                service.submit(() -> {
                    log.debug("task1 start...");
                    sleep(1);
                    latch.countDown();
                });
                service.submit(() -> {
                    log.debug("task2 start...");
                    sleep(2);
                    latch.countDown();
                });
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("task1 task2 finish...");
            }
            service.shutdown();
        }
    }
    

    这里就能看出CountDownLatch和CyclicBarrier的区别了,CountDownLatch不能重复使用,CyclicBarrier可以,实现的效果是一样的。

    image-20201231153511099

    CyclicBarrier 和 CountDownLatch 的异同

    下面我们来总结一下 CyclicBarrier 和 CountDownLatch 有什么异同。

    相同点:都能阻塞一个或一组线程,直到某个预设的条件达成发生,再统一出发。

    但是它们也有很多不同点,具体如下。

    作用对象不同:CyclicBarrier 要等固定数量的线程都到达了栅栏位置才能继续执行,而 CountDownLatch 只需等待数字倒数到 0,也就是说 CountDownLatch 作用于事件,但 CyclicBarrier 作用于线程;CountDownLatch 是在调用了 countDown 方法之后把数字倒数减 1,而 CyclicBarrier 是在某线程开始等待后把计数减 1。

    可重用性不同:CountDownLatch 在倒数到 0 并且触发门闩打开后,就不能再次使用了,除非新建一个新的实例;而 CyclicBarrier 可以重复使用,并不需要重新新建实例。CyclicBarrier 还可以随时调用 reset 方法进行重置,如果重置时有线程已经调用了 await 方法并开始等待,那么这些线程则会抛出 BrokenBarrierException 异常。

    执行动作不同:CyclicBarrier 有执行动作 barrierAction,而 CountDownLatch 没这个功能。

  • 相关阅读:
    IE下JS文件失效问题总结
    什么是RFC?
    CHROME对CSS的解析
    php_network_getaddresses: getaddrinfo failed
    Fedora10下配置Apache和虚拟主机
    Apache的Charset设置
    网页设计中的面包屑路径
    利用JS实现的根据经纬度计算地球上两点之间的距离
    【OpenCV学习】子矩阵操作
    【OpenCV学习】ROI区域
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14217182.html
Copyright © 2011-2022 走看看