• Java—CountDownLatch使用详解


    CountDownLatch介绍

    CountDownLatch概述

    1. CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。
    2. 有一点要说明的是CountDownLatch初始化后计数器值递减到0的时候,不能再复原的,这一点区别于SemaphoreSemaphore是可以通过release操作恢复信号量的。

    CountDownLatch使用原理

    使用原理

    1. 创建CountDownLatch并设置计数器值。
    2. 启动多线程并且调用CountDownLatch实例的countDown()方法。
    3. 主线程调用 await() 方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务,count值为0,停止阻塞,主线程继续执行。

    使用模板

    public class CountDownLatchModule {
    
        //线程数
        private static int N = 10;
    
        // 单位:min
        private static int countDownLatchTimeout = 5;
    
        public static void main(String[] args) {
            //创建CountDownLatch并设置计数值,该count值可以根据线程数的需要设置
            CountDownLatch countDownLatch = new CountDownLatch(N);
    
    		//创建线程池
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    
            for (int i = 0; i < N; i++) {
                cachedThreadPool.execute(() ->{
                    try {
                        System.out.println(Thread.currentThread().getName() + " do something!");
                    } catch (Exception e) {
                        System.out.println("Exception: do something exception");
                    } finally {
                        //该线程执行完毕-1
                        countDownLatch.countDown();
                    }
                });
            }
    
            System.out.println("main thread do something-1");
            try {
                countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                System.out.println("Exception: await interrupted exception");
            } finally {
                System.out.println("countDownLatch: " + countDownLatch.toString());
            }
            System.out.println("main thread do something-2");
            //若需要停止线程池可关闭;
    //        cachedThreadPool.shutdown();
    
        }
    

    运行结果:

    main thread do something-1
    pool-1-thread-1 do something!
    pool-1-thread-2 do something!
    pool-1-thread-3 do something!
    pool-1-thread-5 do something!
    pool-1-thread-6 do something!
    pool-1-thread-7 do something!
    pool-1-thread-8 do something!
    pool-1-thread-4 do something!
    pool-1-thread-9 do something!
    pool-1-thread-10 do something!
    countDownLatch: java.util.concurrent.CountDownLatch@76fb509a[Count = 0]
    main thread do something-2
    
    

    CountDownLatch常用方法

    在这里插入图片描述

    • public void await() throws InterruptedException:调用await()方法的线程会被挂起,等待直到count值为0再继续执行。
    • public boolean await(long timeout, TimeUnit unit) throws InterruptedException:同await(),若等待timeout时长后,count值还是没有变为0,不再等待,继续执行。时间单位如下常用的毫秒、天、小时、微秒、分钟、纳秒、秒。
      在这里插入图片描述
    • public void countDown(): count值递减1.
    • public long getCount():获取当前count值。
    • public String toString():重写了toString()方法,多打印了count值,具体参考源码。

    CountDownLatch使用场景

    一个程序中有N个任务在执行,我们可以创建值为N的CountDownLatch,当每个任务完成后,调用一下countDown()方法进行递减count值,再在主线程中使用await()方法等待任务执行完成,主线程继续执行。

    CountDownLatch源码

    构造方法源码

        /**
         * Constructs a {@code CountDownLatch} initialized with the given count.
         *
         * @param count the number of times {@link #countDown} must be invoked
         *        before threads can pass through {@link #await}
         * @throws IllegalArgumentException if {@code count} is negative
         */
        public CountDownLatch(int count) {
            if (count < 0) throw new IllegalArgumentException("count < 0");
            this.sync = new Sync(count);
        }
    

    toString()方法源码

        /**
         * Returns a string identifying this latch, as well as its state.
         * The state, in brackets, includes the String {@code "Count ="}
         * followed by the current count.
         *
         * @return a string identifying this latch, as well as its state
         */
        public String toString() {
            return super.toString() + "[Count = " + sync.getCount() + "]";
        }
    

    CountDownLatch示例

    作为线程启动信号

    代码

    public class CountDownLatchTest {
    
        /**
         * a start signal that prevents any worker from proceeding
         * until the driver is ready for them to proceed;
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch startSignal = new CountDownLatch(1);
            CountDownLatch doneSignal = new CountDownLatch(10);
            for (int i = 0; i < 10; i++) {
                // create and start threads
                new Thread(new Worker(startSignal, doneSignal)).start();
            }
            // don't let run yet
            System.out.println("do something else 1");
            // let all threads proceed
            startSignal.countDown();
            System.out.println("do something else 2");
            // wait for all to finish
            doneSignal.await();
            System.out.println("wait for all to finsh");
        }
    
        static class Worker implements Runnable{
    
            private final CountDownLatch startSignal;
            private final CountDownLatch doneSignal;
    
            Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
                this.startSignal = startSignal;
                this.doneSignal = doneSignal;
            }
    
            @Override
            public void run() {
                try {
                    startSignal.await();
                    doWork();
                    doneSignal.countDown();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
    
            void doWork() {
                System.out.println("do work!");
            }
        }
    
    }
    

    运行结果

    do something else 1
    do something else 2
    do work!
    do work!
    do work!
    do work!
    do work!
    do work!
    do work!
    do work!
    do work!
    do work!
    wait for all to finsh
    

    从运行结果可以看出:

    1. 主线程先打印do something else 1do something else 2。因为startSignal.countDown();完后,count才为0,子线程才能打印。
    2. 因为startSignal.await();是在子线程内,所有子线程都等待startSignal.countDown()执行后才能打印do work!
    3. doneSignal.await();等待所有子线程执行后,每次都doneSignal.countDown(),最后count为0,主线程才执行打印wait for all to finsh

    作为线程等待完成信号

    代码

    public class CountDownLatchTest2 {
    
        /**
         * a completion signal that allows the driver to wait
         * until all workers have completed.
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch doneSignal = new CountDownLatch(5);
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {
                // create and start threads
                cachedThreadPool.execute(new Worker(doneSignal, i));
            }
            // don't let run yet
            System.out.println("do something else 1");
            // wait for all to finish
            doneSignal.await();
            System.out.println("===========================count: " + doneSignal.getCount());
            System.out.println("do something else 2");
            cachedThreadPool.shutdown();
        }
    
        static class Worker implements Runnable{
    
            private final CountDownLatch doneSignal;
            private final int i;
    
            Worker(CountDownLatch doneSignal, int i) {
                this.doneSignal = doneSignal;
                this.i = i;
            }
    
            @Override
            public void run() {
                try {
                    doWork();
                    doneSignal.countDown();
                    System.out.println("i = " + i + ", " + doneSignal.toString());
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
    
            void doWork() {
                System.out.println("do work!");
            }
        }
    
    }
    

    运行结果

    do something else 1
    do work!
    i = 0, java.util.concurrent.CountDownLatch@128abd43[Count = 4]
    do work!
    i = 1, java.util.concurrent.CountDownLatch@128abd43[Count = 3]
    do work!
    i = 2, java.util.concurrent.CountDownLatch@128abd43[Count = 2]
    do work!
    i = 3, java.util.concurrent.CountDownLatch@128abd43[Count = 1]
    do work!
    i = 4, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    ===========================count: 0
    do something else 2
    do work!
    i = 5, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    do work!
    i = 6, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    do work!
    i = 7, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    do work!
    i = 8, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    do work!
    i = 9, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
    

    从运行结果可以看出,主线程是等待其他线程运行了5次结束后就打印了do something else 2信息,因为CountDownLatch数值为5。

  • 相关阅读:
    应用程序跳转
    百度地图集成
    导航 -MapKit
    导航
    定位
    ApexSql Log使用体会
    Oracle学习 第16天
    上来冒个泡吧
    Oracle学习 实战心得总结
    好久没上来冒个泡了,整整一个半月
  • 原文地址:https://www.cnblogs.com/Andya/p/12925634.html
Copyright © 2020-2023  润新知