美文网首页
高并发(8)- 线程并发工具类-CountDownLatch

高并发(8)- 线程并发工具类-CountDownLatch

作者: 残冬十九 | 来源:发表于2020-04-02 07:22 被阅读0次

    @[TOC](高并发(8)- 线程并发工具类-CountDownLatch)

    前言

        上篇文章讲解了线程的并发工具类之ForkJoin,本文就来讲解CountDownLatch并发工具类
        ForkJoin的核心思想是分而治之
        CountDownLatch的则是一组线程等待其他的线程完成工作以后在执行。
    

    什么是CountDownLatch

    CountDownLatch是一组线程等待其他线程工作完成以后在执行。例如:一个框架在启动的时候,需要加载各种功能,只有当这些功能加载完成之后,才可以运行主线程,这个时候就可以用到CountDownLatch了,CountDownLatch通过计数器来实现,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了

    CountDownLatch执行流程

    如图所示,我们定义了一个计数器cnt = 5,TW1和TW2两个线程执行了await()方法,就需要等待计数器减少为0.

    • 所以Td执行了一个操作,CountDown了一次,cnt-1=4
    • Tb线程也操作了一次,CountDown了一次,cnt-1=3
    • Td线程又操作了一次,CountDown了一次,cnt-1=2
    • Ta线程也操作了一次,CountDown了一次,cnt-1=1
    • Tc线程也操作了一次,CountDown了一次,cnt-1=0
      因为这时候cnt计数器=0所以,TW1和TW2两个线程也便推出了等待,继续运行

    注意

    1. 一个CountDownLatch可以被多个线程等待,而不是只能被一个线程使用,如图所示TW1和TW2用的同一个CountDownLatch。
    2. 一个线程也可以多次CountDown操作,如图所示,Td线程进行了两次CountDown操作。
    3. 一个线程在CountDown之后还是可以继续运行的,不会在CountDown之后停止,如图所示,Ta和Td两个线程在CountDown之后继续运行。

    CountDownLatch实现

    • countDownLatch类中只提供了一个构造器:
    //参数count为计数值
    public CountDownLatch(int count) {}; 
    
    • countDownLatch类中还有三个方法比较重要
    //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
    public void await() throws InterruptedException { };   
    //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
    //将count值减1
    public void countDown() { };  
    
    • 具体代码实现
    /**
     * @version 1.0
     * @Description CountDownLatchDemo
     * @Author wb.yang
     */
    public class CountDownLatchDemo {
    
        /**
         * 创建一个计数器为6的CountDownLatch
         */
        static CountDownLatch latch = new CountDownLatch(6);
    
    
        /**
         * 初始化线程(只有一步,有4个)
         */
        private static class InitThread implements Runnable {
    
            @Override
            public void run() {
                System.out.println("Thread_" + Thread.currentThread().getId()
                        + " 准备初始化工作......");
                //初始化线程完成工作了,countDown方法只扣减一次;
                latch.countDown();
                System.out.println("Thread_" + Thread.currentThread().getId()
                        + " .继续做剩余的工作");
            }
        }
    
        /**
         * 业务线程
         */
        private static class BusinessThread implements Runnable {
    
            @Override
            public void run() {
                try {
                    // 等待
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("BusiThread_" + Thread.currentThread().getId()
                        + "运行中-----");
            }
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            //单独的初始化线程,初始化分为2步,需要扣减两次
            new Thread(new Runnable() {
                @Override
                public void run() {
                    SleepTools.ms(1);
                    System.out.println("Thread_" + Thread.currentThread().getId()
                            + "准备初始化工作第一步......");
                    latch.countDown();//每完成一步初始化工作,扣减一次
                    System.out.println("开始第二步工作.......");
                    SleepTools.ms(1);
                    System.out.println("Thread_" + Thread.currentThread().getId()
                            + "准备初始化工作第二步......");
                    latch.countDown();//每完成一步初始化工作,扣减一次
                }
            }).start();
            //开始主要业务线程
            new Thread(new BusinessThread()).start();
            for (int i = 0; i <= 3; i++) {
                //执行三次初始化工作
                Thread thread = new Thread(new InitThread());
                thread.start();
            }
    
            latch.await();
            System.out.println("main方法工作........");
        }
    }
    
    

    代码中可以看到,我们定义了一个CountDownLatch,计数器为6,然后以一个业务线程,需要等待CountDownLatch执行完之后才能执行。还有六个初始化线程。

    1. 运行一个初始化线程
    2. 运行业务线程
    3. 进行剩下的四初始化线程
    4. 主线程等待await,等待CountDownLatch

    再看下运行结果


    CountDownLatch运行结果

    我们可以看到,先是初始化线程运行,进行了两次CountDown,证明一个线程可以多次CountDown,然后是剩下四个初始化线程进行CountDown操作,并且在CountDown操作后技术执行代码,然后是主业务线程在CountDown为0的时候执行,由于主线程也是await,所以也需要CountDown为0才可以执行,也证明了一个CountDownLatch可以被多个线程使用。

    相关文章

      网友评论

          本文标题:高并发(8)- 线程并发工具类-CountDownLatch

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