示例一、
原文:https://www.relaxheart.cn/to/master/blog?uuid=83
CountDownLatch是一个非常实用的多线程控制工具类。常用的就下面几个方法:
CountDownLatch(int count) //实例化一个倒计数器,count指定计数个数
countDown() // 计数减一
await() //等待,当计数减到0时,所有线程并行执行
CountDownLatch在我工作的多个场景被使用,算是用的很频繁的了,比如我司API接口要求响应时间在200ms以内,但是如果一个接口内部依赖多个三方/外部服务,那串行调用接口的RT必然很久,所以个人用的最多的是接口RT优化场景,内部服务并行调用。
对于倒计数器,一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检测。只有等到所有的检查完毕后,引擎才能点火。那么在检测环节当然是多个检测项可以同时进行的。代码实现:
/**
* @Author: 王琦 <QQ.Eamil>1124602935@qq.com</QQ.Eamil>
* @Date: 2019-5-4 0004 9:43
* @Description: 倒计时器示例:火箭发射
*/
public class CountDownLatchDemo implements Runnable{
static final CountDownLatch latch = new CountDownLatch(10);
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
// 模拟检查任务
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//计数减一
//放在finally避免任务执行过程出现异常,导致countDown()不能被执行
latch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i=0; i<10; i++){
exec.submit(demo);
}
// 等待检查
latch.await();
// 发射火箭
System.out.println("Fire!");
// 关闭线程池
exec.shutdown();
}
}
上述代码中我们先生成了一个CountDownLatch实例。计数数量为10,这表示需要有10个线程来完成任务,等待在CountDownLatch上的线程才能继续执行。latch.countDown();方法作用是通知CountDownLatch有一个线程已经准备完毕,倒计数器可以减一了。latch.await()方法要求主线程等待所有10个检查任务全部准备好才一起并行执行。
示例二、
CountDownLatch是java.util.concurrent包中一个类,CountDownLatch只要提供的机制是多个(具体数量等于初始化CountDownLatch时count的值)线程都达到了预期状态或者完成了预期工作时触发事件,其他线程可以等待这个事件来触发自己后续的工作。等待的线程可以是多个,即CountDownLatch可以唤醒多个等待的线程。到达自己预期状态的线程会调用CountDownLatch的countDown方法,而等待的线程会调用CountDownLatch的await方法。
结合以下例子,可以快速掌握这个这个类的基本使用方法:
例2.1:
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDown = new CountDownLatch(1);
CountDownLatch await = new CountDownLatch(5);
// 依次创建并启动处于等待状态的5个MyRunnable线程
for (int i = 0; i < 5; ++i) {
new Thread(new MyRunnable(countDown, await)).start();
}
System.out.println("用于触发处于等待状态的线程开始工作......");
System.out.println("用于触发处于等待状态的线程工作完成,等待状态线程开始工作......");
countDown.countDown();
await.await();
System.out.println("Bingo!");
}
public class MyRunnable implements Runnable {
private final CountDownLatch countDown;
private final CountDownLatch await;
public MyRunnable(CountDownLatch countDown, CountDownLatch await) {
this.countDown = countDown;
this.await = await;
}
public void run() {
try {
countDown.await();//等待主线程执行完毕,获得开始执行信号...
System.out.println("处于等待的线程开始自己预期工作......");
await.countDown();//完成预期工作,发出完成信号...
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
用于触发处于等待状态的线程开始工作......
用于触发处于等待状态的线程工作完成,等待状态线程开始工作......
处于等待的线程开始自己预期工作......
处于等待的线程开始自己预期工作......
处于等待的线程开始自己预期工作......
处于等待的线程开始自己预期工作......
处于等待的线程开始自己预期工作......
Bingo!
例2.2:
public class TestDemo2 {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
int count = 10;
final CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
threadPool.execute(new MyRunnable1(latch, i));
}
latch.await();
System.err.println("等待线程被唤醒!");
threadPool.shutdown();
}
}
class MyRunnable1 implements Runnable {
CountDownLatch latch = null;
int i;
public MyRunnable1(CountDownLatch latch, int i) {
this.latch = latch;
this.i = i;
}
@Override
public void run() {
System.err.println("线程" + i +"完成了操作...");
try {
Thread.currentThread();
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
}
运行结果:
线程0完成了操作...
线程3完成了操作...
线程2完成了操作...
线程1完成了操作...
线程4完成了操作...//暂停4秒
线程5完成了操作...
线程6完成了操作...
线程8完成了操作...
线程7完成了操作...
线程9完成了操作...//暂停4秒
等待线程被唤醒!