美文网首页
线程并发工具类之CountDownLatch

线程并发工具类之CountDownLatch

作者: 传达室马大爷 | 来源:发表于2020-04-06 13:54 被阅读0次

    CountDownLatch

    • 一个线程等待另外多个线程完成后,在继续执行,有点类似于join()方法
    • CountdDwonLatch是通过计数器来实现的,初始化CountDownLatch实例时需要设置计数器的个数
    • CountdDwonLatch中主要用到两个方法,调用await()方法的线程处于阻塞状态,每调用一次countDown()方法,则计数器减1,直到计数器值为0时,await()方法所阻塞的线程会继续执行
    • 调用await()阻塞的线程可以不止一个,可以多个线程都调用await()进行阻塞,等满足条件时执行
    实例
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ThreadLocalRandom;
    
    import com.shawntime.enjoy.architect.concurrency.SleepUtils;
    
    /**
     * countDownLatch:
     */
    public class CountDownLatchTest {
    
        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(6);
    
            for (int i = 0; i < 4; ++i) {
                InitThread initThread = new InitThread("init-thread-" + i, countDownLatch);
                initThread.start();
            }
    
            BusinessThread businessThread = new BusinessThread("business-thread", countDownLatch);
            businessThread.start();
    
            new Thread(() -> {
                {
                    try {
                        SleepUtils.sleepBySeconds(1);
                        System.out.println(Thread.currentThread().getName() + " really init worker step 1...");
                    } finally {
                        countDownLatch.countDown();
                    }
                    try {
                        SleepUtils.sleepBySeconds(1);
                        System.out.println(Thread.currentThread().getName() + " really init worker step 2...");
                    } finally {
                        countDownLatch.countDown();
                    }
    
                }
            }, "single-thread").start();
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("Main do work....");
        }
    
    
        /**
         * 初始化线程
         */
        private static class InitThread extends Thread {
    
            private CountDownLatch countDownLatch;
    
            public InitThread(String name, CountDownLatch countDownLatch) {
                super(name);
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + " waiting really init...");
                    int time = ThreadLocalRandom.current().nextInt(1000);
                    SleepUtils.sleepByMilliSeconds(time);
                } finally {
                    countDownLatch.countDown();
                }
                for (int i = 0; i < 3; ++i) {
                    System.out.println(Thread.currentThread().getName() + " init continue working...");
                }
            }
        }
    
        /**
         * 业务线程
         */
        private static class BusinessThread extends Thread {
    
            private CountDownLatch countDownLatch;
    
            public BusinessThread(String name, CountDownLatch countDownLatch) {
                super(name);
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 3; ++i) {
                    System.out.println(Thread.currentThread().getName() + " business do working...");
                }
            }
        }
    }
    

    运行结果:
    init-thread-1 waiting really init...
    init-thread-0 waiting really init...
    init-thread-2 waiting really init...
    init-thread-3 waiting really init...
    init-thread-0 init continue working...
    init-thread-0 init continue working...
    init-thread-0 init continue working...
    init-thread-1 init continue working...
    init-thread-1 init continue working...
    init-thread-1 init continue working...
    init-thread-2 init continue working...
    init-thread-2 init continue working...
    init-thread-2 init continue working...
    init-thread-3 init continue working...
    init-thread-3 init continue working...
    init-thread-3 init continue working...
    single-thread really init worker step 1...
    single-thread really init worker step 2...
    Main do work....
    business-thread business do working...
    business-thread business do working...
    business-thread business do working...

    结果分析

    1、countDownLatch计数器个数为6,则必须等到调用了6次countDown()方法之后主线程和业务线程才会执行
    2、主线程和业务线程同时调用了await()方法进行阻塞

    countDownLatch()方法和join()方法的区别

    • join():调用某个thread的join()方法,当前线程会被阻塞,直到thread线程结束才会继续执行,join()的原理是不断检测thread线程是否存活,如果thread一直存活则会继续等待直到thread线程结束后notifyAll()方法被调用
    实例:实现数据的初始化工作,主线程需要等待线程1、2、3均初始化完成后才能继续执行

    countDownLatch()方法实现

    import java.util.concurrent.CountDownLatch;
    
    import com.shawntime.enjoy.architect.concurrency.SleepUtils;
    
    public class DataInitForCountDownLatch {
    
        private static class InitThread extends Thread {
    
            private CountDownLatch countDownLatch;
    
            public InitThread(String name, CountDownLatch countDownLatch) {
                super(name);
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    SleepUtils.sleepByMilliSeconds(100);
                    System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");
    
                    SleepUtils.sleepByMilliSeconds(100);
                    System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
                } finally {
                    countDownLatch.countDown();
                }
            }
        }
    
        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(3);
            for (int i = 1; i < 4; ++i) {
                new InitThread("thread-" + i, countDownLatch).start();
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程执行....");
        }
    }
    

    输出结果:
    thread-3线程初始化第一步完成
    thread-1线程初始化第一步完成
    thread-2线程初始化第一步完成
    thread-1线程初始化第二步完成
    thread-2线程初始化第二步完成
    thread-3线程初始化第二步完成
    主线程执行....

    join()方法实现

    import com.shawntime.enjoy.architect.concurrency.SleepUtils;
    
    public class DataInitForJoin {
    
        private static class InitThread extends Thread {
    
            public InitThread(String name) {
                super(name);
            }
    
            @Override
            public void run() {
    
                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");
    
                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            InitThread initThread1 = new InitThread("thread-1");
            InitThread initThread2 = new InitThread("thread-2");
            InitThread initThread3 = new InitThread("thread-3");
    
            initThread1.start();
            initThread2.start();
            initThread3.start();
    
            initThread1.join();
            initThread2.join();
            initThread3.join();
    
            System.out.println("主线程继续执行....");
        }
    }
    

    输出结果:
    thread-2线程初始化第一步完成
    thread-1线程初始化第一步完成
    thread-3线程初始化第一步完成
    thread-2线程初始化第二步完成
    thread-1线程初始化第二步完成
    thread-3线程初始化第二步完成
    主线程继续执行....

    如果我想在初始化第一步完成结束后主线程就可以继续执行的话,则join()方法无法实现,而countDownLatch则可以轻松的实现

    import java.util.concurrent.CountDownLatch;
    
    import com.shawntime.enjoy.architect.concurrency.SleepUtils;
    
    public class DataInitForCountDownLatch {
    
        private static class InitThread extends Thread {
    
            private CountDownLatch countDownLatch;
    
            public InitThread(String name, CountDownLatch countDownLatch) {
                super(name);
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    SleepUtils.sleepByMilliSeconds(100);
                    System.out.println(Thread.currentThread().getName() + "线程初始化第一步完成");
                } finally {
                    countDownLatch.countDown();
                }
                SleepUtils.sleepByMilliSeconds(100);
                System.out.println(Thread.currentThread().getName() + "线程初始化第二步完成");
            }
        }
    
        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(3);
            for (int i = 1; i < 4; ++i) {
                new InitThread("thread-" + i, countDownLatch).start();
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程执行....");
        }
    }
    

    输出结果:
    thread-1线程初始化第一步完成
    thread-3线程初始化第一步完成
    thread-2线程初始化第一步完成
    主线程执行....
    thread-1线程初始化第二步完成
    thread-3线程初始化第二步完成
    thread-2线程初始化第二步完成

    总结:join()方法必须要等到线程结束后才能解除阻塞,而countDownLatch不需要等到线程结束,只要调用了countDown()方法保证计数器个数为0时即可解除阻塞,countDownLatch比join()方法更灵活

    相关文章

      网友评论

          本文标题:线程并发工具类之CountDownLatch

          本文链接:https://www.haomeiwen.com/subject/swxbphtx.html