学习使我快乐---CountDownLatch的典型应用

前言

面试官:下面有这么一个场景“模拟100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束!” 该怎么实现?
Peter:额… 用多线程;
面试官:那请你具体说下把!
Peter:GG

说起这个我还是在去年面试中遇到的,当时年少无知,不晓得这个,最后去了网上学了一圈才发现这个真是yyds,分享给大家我的理解,欢迎大家提出好的意见,不多说直接上代码


一、CountDownLatch是什么?

简单来说,CountDownLatch就是一个同步工具类,是用来协调多个线程之间的同步,或者说起到线程之间的通信。本质上其实就是通过一个计数器来实现的,初始值为线程的数量。每当一个线程完成了自己的任务,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。

二、案例分析

1.代码示例

代码如下(示例):

package org.thread;

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

/**
 * 例子:模拟100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束
 */

public class thread_run_test {

    public static class Player implements Callable {
        @Override
        public Object call() throws Exception {
            System.out.println("工作人员:"+ Thread.currentThread().getName() + " 准备就绪!");
            CountDownLatchUtil.startSingal.await();
            System.out.println("解说员:"+ Thread.currentThread().getName() + " 开始起跑!");
            Thread.sleep((long) (Math.random() * 1000));
            System.out.println("解说员:"+ Thread.currentThread().getName() + " 到达终点!");
            CountDownLatchUtil.endSingal.countDown();
            return null;
        }
    }

    public static class CountDownLatchUtil{
        //开始信号
        public static CountDownLatch startSingal = new CountDownLatch(1);
        //结束信号
        public static CountDownLatch endSingal = new CountDownLatch(10);

        public static void main(String[] args) throws InterruptedException {
            //可以往线程池放n个任务,但每次执行只执行10个任务,别的任务在排队
            ExecutorService service = Executors.newFixedThreadPool(10);
            for (int i = 0;i<10;i++){
                service.submit(new Player());
            }
            Thread.sleep(5*1000);
            System.out.println("裁判员:开始!");
            startSingal.countDown();
            endSingal.await();
            System.out.println("裁判员:结束!");
            service.shutdown();
        }
    }
}

2.输出结果

代码如下(示例):

工作人员:pool-1-thread-1 准备就绪!
工作人员:pool-1-thread-2 准备就绪!
工作人员:pool-1-thread-3 准备就绪!
工作人员:pool-1-thread-4 准备就绪!
工作人员:pool-1-thread-5 准备就绪!
工作人员:pool-1-thread-6 准备就绪!
工作人员:pool-1-thread-7 准备就绪!
工作人员:pool-1-thread-8 准备就绪!
工作人员:pool-1-thread-9 准备就绪!
工作人员:pool-1-thread-10 准备就绪!
裁判员:开始!
解说员:pool-1-thread-1 开始起跑!
解说员:pool-1-thread-5 开始起跑!
解说员:pool-1-thread-4 开始起跑!
解说员:pool-1-thread-7 开始起跑!
解说员:pool-1-thread-3 开始起跑!
解说员:pool-1-thread-8 开始起跑!
解说员:pool-1-thread-9 开始起跑!
解说员:pool-1-thread-2 开始起跑!
解说员:pool-1-thread-10 开始起跑!
解说员:pool-1-thread-6 开始起跑!
解说员:pool-1-thread-6 到达终点!
解说员:pool-1-thread-8 到达终点!
解说员:pool-1-thread-4 到达终点!
解说员:pool-1-thread-2 到达终点!
解说员:pool-1-thread-7 到达终点!
解说员:pool-1-thread-9 到达终点!
解说员:pool-1-thread-3 到达终点!
解说员:pool-1-thread-10 到达终点!
解说员:pool-1-thread-1 到达终点!
解说员:pool-1-thread-5 到达终点!
裁判员:结束!

3.个人理解

其实主要用到的点就是线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化成new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1,countdownLatch.countDown(),当计数器的值变为0时,CountDownLatch上await()的线程就会呗唤醒。

结合到实例赛跑中,我们将多个线程设置好线程数后放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDown(1),将计算器初始化为1,多个线程在开始执行任务钱首先countdownLatch.await(),当主线调用countDown()时,计数器变为0.多个线程同时被唤醒。然后开始执行比赛的过程,多个线程继续工作,当每个线程都执行到结束信号countDown方法时,计数器归0,又重新唤醒到主线程的await(),主线程继续,从而实现了所有进程结束后,主线上的裁判才会宣布比赛结束。


方法简要说明

public void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少;每调用一次计数器 值-1,直到count被减为0,代表所有线程全部执行完毕。

await(): 等待计数器变为0,即等待所有异步线程执行完毕。

CountDownLatch(int count):count为计数器的初始值(一般需要多少个线程执行,count就设为几)。

public boolean await(long timeout,TimeUnit unit) throws InterruptedException:使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true值。

getCount():获取当前计数器的值。

总结

当然,CountDownLatch也是有不足之处的,CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用;但总体来说CountDownLatch在我看来对使用者而言,只需要传入一个int型变量控制任务数量即可,更加方便使用。理解不对的地方欢迎大家指导纠正,共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值