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

2020-04-02  本文已影响0人  残冬十九

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

前言

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

什么是CountDownLatch

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

CountDownLatch执行流程

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

注意

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

CountDownLatch实现

//参数count为计数值
public CountDownLatch(int count) {}; 
//调用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可以被多个线程使用。

上一篇下一篇

猜你喜欢

热点阅读