前言
面试官:下面有这么一个场景“模拟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型变量控制任务数量即可,更加方便使用。理解不对的地方欢迎大家指导纠正,共同进步!