这里借鉴了一篇总结得不错的博客:https://blog.csdn.net/joenqc/article/details/76794356
CountDownLatch的概念
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。
CountDownLatch的用法
CountDownLatch典型用法1:某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为n new CountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减1 countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
CountDownLatch典型用法2:实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。
这里根据用法二,举个简单的例子:
public class Single {
private Single(){ ... }
public static Single getInstance() { ... }
return 0;
}
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(1); //CountDownLatch传入线程数(1个任务)
int threadCount = 1000;
for (int i = 0; i < threadCount; i++) {
new Thread() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Single.getInstance().hashCode());
}
}.start();
}
latch.countDown();
}
CountDownLatch的不足
CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。
使用场景:
1、比如说需要去解析一个excel的数据,为了更快的解析则每个sheet都使用一个线程去进行解析,但是最后的汇总数据的工作则需要等待每个sheet的解析工作完成之后才能进行,不同大小的excel表读取所需的时间长短不一,但是需要等待所有的表都读取完了才可以进行汇总,这时就快的线程就要等待慢的线程完成,这就可以使用CountDownLatch。
参考:https://blog.csdn.net/LightOfMiracle/article/details/73456832

public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException{
CountDownLatch countDownLatch = new CountDownLatch(2){
@Override
public void await() throws InterruptedException { //重写await()方法
super.await();
System.out.println(Thread.currentThread().getName() + " count down is ok");
}
};
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
//do something
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is done");
countDownLatch.countDown(); //表格1读取完成,调用countDown(),正在进行的任务线程-1
}
}, "thread1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
//do something
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is done");
countDownLatch.countDown(); //表格2读取完成,调用countDown(),正在进行的任务线程-1
}
}, "thread2");
thread1.start();
thread2.start();
countDownLatch.await(); //在两个线程完成之后,汇总进行之前,需要调用await()
}
}
2、一群人每天也要上班,话说今天定了个饭店,一起吃个饭,通知大家下班去饭店集合。每个人人在不同的地方上班,必须等到所有人人到场才能吃饭。
参考:https://blog.csdn.net/lmj623565791/article/details/26626391
public class CountDownLatchDemo {
private static CountDownLatch countDownLatch = new CountDownLatch(3); //3个任务,3个线程
public static void main(String[] args)
{
new Thread()
{
public void run()
{
friend1(); //朋友1去饭店所需要的过程
latch.countDown(); //第一个人到达饭店,任务线程1完成
};
}.start();
new Thread()
{
public void run()
{
friend2(); //朋友2去饭店所需要的过程
latch.countDown(); //第二个人到达饭店,任务线程2完成
};
}.start();
new Thread()
{
public void run()
{
friend3(); //朋友3去饭店所需要的过程
latch.countDown(); //第三个人到达饭店,任务线程3完成
};
}.start();
latch.await(); //所有的线程完成了,在进行下一步(吃饭)之前需要调用await()
eatTogether(); //人到齐了,恰饭!
}
}