CountDownLatch详解-线程顺序控制
一、概念
1、countDownLatch是在java1.5被引入,存在于java.util.cucurrent包下。
2、countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
3、是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
二、CountDownLatch 常用方法说明
CountDownLatch(int count); //构造方法,创建一个值为count 的计数器。
await();//阻塞当前线程,将当前线程加入阻塞队列。
await(long timeout, TimeUnit unit);//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,
countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
三、测试代码
业务场景:
统计用户数据、订单数和商品情况,然后统一处理返回结果。如果每个业务场景预计要5秒,单线程情况需要15秒左右,如果用多线程处理,费时5秒左右。在多线程情况下,如何保证多线程统一执行完毕并返回结果呢?
private static Map<String,Object> map = new HashMap<>();
//初始化计数器3
private static CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println("开始时间:"+start);
//统计新增用户
Thread userThread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程一:统计新增用户开始");
Thread.sleep(5000);
map.put("newUser",10);
System.out.println("线程一:统计新增用户结束");
//计数器减一
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//统计订单
Thread orderThread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程二:统计订单开始");
Thread.sleep(5000);
map.put("orderNum",20);
System.out.println("线程二:统计订单结束");
//计数器减一
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//统计商品
Thread goodThread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程三:统计商品开始");
Thread.sleep(5000);
map.put("goodCount",100);
System.out.println("线程三:统计商品结束");
//计数器减一
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
userThread.start();
orderThread.start();
goodThread.start();
try {
//主线程等待,countDownLatch为0的时候,也就是上面线程全都执行完毕,再执行下面的业务
countDownLatch.await();
System.out.println("统计完毕,统计结果:"+ JSON.toJSONString(map));
long end = System.currentTimeMillis();
System.out.println("结束时间:"+end+";耗时:"+(end-start));
} catch (Exception e) {
e.printStackTrace();
}
}
执行结果一
主线程会等到其他统计线程执行完毕以后才执行
开始时间:1601634626698
线程一:统计新增用户开始
线程二:统计订单开始
线程三:统计商品开始
线程二:统计订单结束
线程一:统计新增用户结束
线程三:统计商品结束
统计完毕,统计结果:{"goodCount":100,"newUser":10,"orderNum":20}
结束时间:1601634631811;耗时:5113
如果注释 countDownLatch.await();会有什么情况呢?
执行结果二
开始时间:1601634798801
线程一:统计新增用户开始
线程二:统计订单开始
线程三:统计商品开始
统计完毕,统计结果:{}
结束时间:1601634798957;耗时:156
线程一:统计新增用户结束
线程三:统计商品结束
线程二:统计订单结束
await()方法会让当前线程等待,只有countDownLatch的计数器为0时才会执行。如果因为程序异常计数器永远不为0,当前线程就会一会等待。可以考虑使用await(long timeout, TimeUnit unit);等待一段时间后执行。
使用场景:
A业务依赖B、C等业务处理完毕后再执行,可以考虑使用countDownLatch
关注公众号免费领取学习资料~