Java 线程池 & CountDownLatch

2022-06-15 13:49:41

线程池 & CountDownLatch

CountDownLatch

背景
在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用 Thread 类的 join 方法,让主线程等待被 join 的线程执行完之后,主线程才能继续往下执行。
但是在线程池中,由于每个线程不会真正意义上的结束,因此Thread.join()方法无法生效,因此,在线程池中一般应用CountDownLatch来确定执行顺序。

CountDownLatch 主要方法

  1. await() throws InterruptedException:调用该方法的线程等到构造方法传入的 count 被减到 0 时,才能继续往向下执行;
  2. await(long timeout, TimeUnit unit):与上面的 await 方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的 timeout 时间后,不管 count 是否减至为 0,都会继续往下执行;
  3. countDown():使当前 CountDownLatch 初始值 count-1;
  4. long getCount():获取当前 CountDownLatch 的值;

每当一个线程执行完一个指定动作之后,调用countDown() 方法,count-1,当count > 0时,主线程调用 await() 方法,线程阻塞,需等待count变为0;当 count = 0 时,await() 方法失效,主线程不再阻塞,开始继续执行下面的代码。

示例代码

例如:
运动员进行跑步比赛,赛场有1名裁判(main线程)和5名运动员(线程池创建的5个线程),
准备阶段:运动员开始检录直到站上跑到,准备完成,裁判需要所有运动员都准备完毕后才能开始比赛;
发令阶段:所有运动员准备完成后,裁判发令,开始比赛;
比赛阶段:运动员听到发令枪后,开始赛跑;
撞线阶段:运动员冲过终点,完成比赛;
比赛结束:所有运动员完成比赛后,裁判员宣布比赛结束。
各个阶段之间都有先后顺序,利用代码实现如下:

importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;publicclassCountDownLatchTest{publicstaticvoidmain(String[] args)throwsException{ExecutorService runnerPool=Executors.newFixedThreadPool(5);int countStart=1;int countEnd=5;int countReady=5;CountDownLatch startCount=newCountDownLatch(countStart);CountDownLatch endCount=newCountDownLatch(countEnd);CountDownLatch readyCount=newCountDownLatch(countReady);for(int i=0; i<5; i++){
            runnerPool.execute(()->{try{System.out.println(Thread.currentThread()+" 准备。");
                    readyCount.countDown();
                    startCount.await();System.out.println(Thread.currentThread().getName()+" 奔跑。");System.out.println(Thread.currentThread().getName()+" 到达终点。");
                    endCount.countDown();}catch(Exception e){
                    e.printStackTrace();}});}
        readyCount.await();System.out.println(Thread.currentThread().getName()+" 发令开始。");
        startCount.countDown();
        endCount.await();System.out.println(Thread.currentThread().getName()+" 所有人员完成比赛。");
        runnerPool.shutdown();}}

输出结果:

Thread[pool-1-thread-1,5,main] 准备。Thread[pool-1-thread-4,5,main] 准备。Thread[pool-1-thread-3,5,main] 准备。Thread[pool-1-thread-2,5,main] 准备。Thread[pool-1-thread-5,5,main] 准备。
main 发令开始。
pool-1-thread-5 奔跑。
pool-1-thread-5 到达终点。
pool-1-thread-1 奔跑。
pool-1-thread-3 奔跑。
pool-1-thread-3 到达终点。
pool-1-thread-4 奔跑。
pool-1-thread-1 到达终点。
pool-1-thread-2 奔跑。
pool-1-thread-4 到达终点。
pool-1-thread-2 到达终点。
main 所有人员完成比赛。

可以看出,5个线程在执行“准备”打印后,进入阻塞状态,在main线程打印”法令开始“后,各个线程执行后续程序;当所有线程执行完成后,main线程执行“所有人员完成比赛”打印。
完成上述顺序执行依赖与CountDownLatch方法:

  1. 首先,创建CountDownLatch;
int countStart=1;int countEnd=5;int countReady=5;CountDownLatch startCount=newCountDownLatch(countStart);CountDownLatch endCount=newCountDownLatch(countEnd);CountDownLatch readyCount=newCountDownLatch(countReady);
  1. 线程池创建5个线程(5个运动员)后,在执行”准备“打印后,执行 readyCount.countDown() 方法,将 countReady-1,执行readyCount.await() 方法后,线程进入阻塞状态;

  2. 当5个线程(5个运动员)都执行了”准备“后,countReady=0,此时,readyCount.await() 不再阻塞线程,main线程(裁判)继续执行“发令开始”,同时执行 startCount.countDown() 方法,countStart-1;

  3. 在main线程(裁判)“发令”前,5个线程(运动员)中的 startCount.await() 方法生效,使得线程阻塞,而main线程执行后,各个线程继续执行后续代码,即“奔跑” 并“到达终点”,同时执行endCount.countDown(), countEnd-1;

  4. 当5个线程(运动员)都执行完毕后,countEnd=0,此时 endCount.awiat()失效, main线程(裁判)最后执行 “所有人员完成比赛”,整个流程结束。

  • 作者:天堂面包店
  • 原文链接:https://blog.csdn.net/qq_43149840/article/details/124171826
    更新时间:2022-06-15 13:49:41