天天看点

并发工具 CountDownLatch

看看这个博客 更有助于理解闭锁

CountDownLatch:闭锁

基本原理

工作原理,如下图。

并发工具 CountDownLatch

线程TA调用await()方法后便会挂起,T1,T2,T3依次调用countDown()方法进行对cnt进行减1操作,最后count值为0时 ,挂起的线程TA便会被唤醒。

主要的方法

  1. CountDownLatch(int count) :仅有的一个构造函数,参数count为需要倒数的数值;
  2. await():调用该方法的线程会被挂起,直到count为0时,该线程才会继续执行;
  3. countDown():对count数值进行减1操作。

应用场景1:一等多

一个线程等待多个线程执行完才继续自己的操作;可以使用闭锁来保证某些活动到其他活动做完才继续进行。

  1. 确保计算机在其需要的所有资源被初始化之后才继续执行;
  2. 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
  3. 等待直到某个操作所有参与者都准备就绪了继续操作。

demo1

package com.mark.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * author:Mark
 * date:2018/7/31  17:46
 */
@Slf4j
public class CountDownLatchTest1 {
    private final static int threadCount = 20;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);//闭锁
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            executorService.execute(()->{
                try {
                    test(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        log.info("finish");
        executorService.shutdown();
    }

    private static void test(int threadNum) throws InterruptedException {
        Thread.sleep(100);
        log.info("{}", threadNum);
        Thread.sleep(100);
    }
}

           

运行结果

22:56:26.945 [pool-1-thread-19] INFO com.mark.example.aqs.CountDownLatchTest1 - 18

22:56:26.945 [pool-1-thread-8] INFO com.mark.example.aqs.CountDownLatchTest1 - 7

22:56:26.945 [pool-1-thread-12] INFO com.mark.example.aqs.CountDownLatchTest1 - 11

22:56:26.945 [pool-1-thread-9] INFO com.mark.example.aqs.CountDownLatchTest1 - 8

22:56:26.945 [pool-1-thread-15] INFO com.mark.example.aqs.CountDownLatchTest1 - 14

22:56:26.945 [pool-1-thread-13] INFO com.mark.example.aqs.CountDownLatchTest1 - 12

22:56:26.946 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest1 - 0

22:56:26.945 [pool-1-thread-10] INFO com.mark.example.aqs.CountDownLatchTest1 - 9

22:56:26.945 [pool-1-thread-18] INFO com.mark.example.aqs.CountDownLatchTest1 - 17

22:56:26.945 [pool-1-thread-11] INFO com.mark.example.aqs.CountDownLatchTest1 - 10

22:56:26.946 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest1 - 2

22:56:26.946 [pool-1-thread-17] INFO com.mark.example.aqs.CountDownLatchTest1 - 16

22:56:26.945 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest1 - 1

22:56:26.946 [pool-1-thread-20] INFO com.mark.example.aqs.CountDownLatchTest1 - 19

22:56:26.945 [pool-1-thread-14] INFO com.mark.example.aqs.CountDownLatchTest1 - 13

22:56:26.945 [pool-1-thread-7] INFO com.mark.example.aqs.CountDownLatchTest1 - 6

22:56:26.946 [pool-1-thread-6] INFO com.mark.example.aqs.CountDownLatchTest1 - 5

22:56:26.946 [pool-1-thread-16] INFO com.mark.example.aqs.CountDownLatchTest1 - 15

22:56:26.946 [pool-1-thread-4] INFO com.mark.example.aqs.CountDownLatchTest1 - 3

22:56:26.946 [pool-1-thread-5] INFO com.mark.example.aqs.CountDownLatchTest1 - 4

22:56:27.051 [main] INFO com.mark.example.aqs.CountDownLatchTest1 - finish

运行结果分析:程序中声明了一个大小为20的CountDownLatch实例,在线程池中运行20个线程的时候。等到这20个线程全部执行完后再执行

log.info("finish");

。一等多的体现,main线程等待其他的线程之执行完。才继续往下走。

demo2 :显示声明等待时间

package com.mark.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * author:Mark
 * date:2018/7/31  17:46
 */
@Slf4j
public class CountDownLatchTest2 {
    private final static int threadCount = 10;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);//闭锁
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            executorService.execute(()->{
                try {
                    test(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await(20, TimeUnit.MILLISECONDS);//指定时间唤醒--》注意打印finish语句
        log.info("finish");
        executorService.shutdown();//不会立即停止线程池里的线程。而是等待线程池里的线程执行完再关闭线程池
    }

    private static void test(int threadNum) throws InterruptedException {
        Thread.sleep(10);
        log.info("{}", threadNum);
    }
}


           

运行结果:

23:01:28.254 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 0

23:01:28.254 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 2

23:01:28.254 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 1

23:01:28.263 [main] INFO com.mark.example.aqs.CountDownLatchTest2 - finish

23:01:28.269 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 4

23:01:28.269 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 5

23:01:28.269 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 3

23:01:28.280 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 6

23:01:28.280 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 8

23:01:28.280 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 7

23:01:28.291 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 9

应用场景2:多等一

package com.mark.learning.concurrent2.flowcontrol.countdownlatch;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description: 多等一,假设 5个用户等待发红包。
 * @Author: Mark
 * @CreateDate: 2020/2/2 23:29
 * @Version: 1.0
 * @Copyright : 豆浆油条个人非正式工作室
 */
@Slf4j
public class CountDownLatchDemo2_1 {

    private final static int THREAD_COUNT = 5;
    private static CountDownLatch BEGIN_CNT = new CountDownLatch(1);
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);

    public static void main(String[] args) throws InterruptedException {

        //起五个线程模拟五个用户,等待红包、抢红包
        for (int i = 0; i < THREAD_COUNT; i++) {
            int finalI = i+1;
            executorService.execute(() -> {
                try {
                    log.info("用户" + finalI + "打开《我爱我家》群聊,等待群主发红包!!");
                    BEGIN_CNT.await();
                    log.info("用户" + finalI + "开始抢红包!!!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        //群主发红包
        Thread.sleep(5000);
        BEGIN_CNT.countDown();
        executorService.shutdown();
    }

}

           

运行结果

23:48:32.259 [pool-1-thread-2] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户2打开《我爱我家》群聊,等待群主发红包!!
23:48:32.259 [pool-1-thread-5] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户5打开《我爱我家》群聊,等待群主发红包!!
23:48:32.259 [pool-1-thread-4] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户4打开《我爱我家》群聊,等待群主发红包!!
23:48:32.259 [pool-1-thread-1] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户1打开《我爱我家》群聊,等待群主发红包!!
23:48:32.259 [pool-1-thread-3] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户3打开《我爱我家》群聊,等待群主发红包!!
23:48:37.253 [pool-1-thread-4] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户4开始抢红包!!!
23:48:37.253 [pool-1-thread-2] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户2开始抢红包!!!
23:48:37.253 [pool-1-thread-1] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户1开始抢红包!!!
23:48:37.253 [pool-1-thread-3] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户3开始抢红包!!!
23:48:37.253 [pool-1-thread-5] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 用户5开始抢红包!!!
           

总结

  1. CountDownLatch不能够重用,如果需要重新计数,可以考虑用CyclicBarrier或者构建新的CountDownLatch实例;
  2. CountDownLatch实例时,需要传递参数(倒数次数),倒数到0的时候,调用await()方法才会继续执行;

继续阅读