CountDownLatch作为一个同步辅助类,它能够允许一个活多个线程等待,直到计数器为0后,才开始运作。
该功能是在jdk.15之后引入的,这样我们在使用的时候就很方便
里面主要有两个方法
1. countDown : 使计数器减一
2. await :等待计数器减为0后开始运行之后的代码,否则一直处于阻塞状态
有了这两个方法,我们能很方便的加入到实际环境中,比如跑步问题
假设运动会上有5个人正在进行跑步比赛,裁判需要等运动员准备完毕后,发出命令,然后运动员开始跑,等所有的运动员都到达终点后,裁判再统计结果
package com.jacksoft.thread;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class ThreadDownLoadTest {
/**
* 总人数
*/
private static final int P_COUNT = 5;
/**
* 裁判人数
*/
private static final int TOTAL = 1;
public static void main(String[] args) {
/**
* 准备总人数,每准备好一个人,就减一
*/
final CountDownLatch readyCount = new CountDownLatch(P_COUNT);
/**
* 跑完人数计数,完成一个人,计数减一
*/
final CountDownLatch runCount = new CountDownLatch(P_COUNT);
/**
* 发出命令开始跑
*/
final CountDownLatch startRecordCount = new CountDownLatch(TOTAL);
for(int i = 1;i <= P_COUNT;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(5000));
System.out.println("运动员" + Thread.currentThread().getName() + "已经准备完毕,还有" + readyCount.getCount() + "个人正在准备中.....");
readyCount.countDown();
startRecordCount.await();
Thread.sleep(new Random().nextInt(10000));
System.out.println("运动员" + Thread.currentThread().getName() + "到达终点,还有" + runCount.getCount() + "个人在路上");
runCount.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
try {
readyCount.await();
System.out.println("裁判:所有运动员准备完毕,开始跑,等待结果");
startRecordCount.countDown();
runCount.await();
System.out.println("裁判:跑步完毕,统计结果...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
运动员Thread-3已经准备完毕,还有5个人正在准备中.....
运动员Thread-1已经准备完毕,还有4个人正在准备中.....
运动员Thread-0已经准备完毕,还有3个人正在准备中.....
运动员Thread-2已经准备完毕,还有2个人正在准备中.....
运动员Thread-4已经准备完毕,还有1个人正在准备中.....
裁判:所有运动员准备完毕,开始跑,等待结果
运动员Thread-4到达终点,还有5个人在路上
运动员Thread-0到达终点,还有4个人在路上
运动员Thread-2到达终点,还有3个人在路上
运动员Thread-3到达终点,还有2个人在路上
运动员Thread-1到达终点,还有1个人在路上
裁判:跑步完毕,统计结果...
这样就能很方便的完成任务,当然这里并没有考虑实际的因素
比如在调用countDown的时候,要写到finally里面,不然如果出现异常信息,计数器不能减一,那么其他等待它的线程就会一直阻塞中,从而造成系统崩溃。