前言
多线程系列我们前面已经更新过多个章节,强烈建议小伙伴按照顺序学习:
CountDownLatch
是JUC中常见的一种工具类,从类名中我们也能略窥一二这个类的含义,CountDown
字面意思为倒数,latch
有门闩(shuan)的意思,主要是用来控制并发流程,本文将详细介绍它的场景和用法。
一、CountDownLatch两大适用场景
- 控制子流程结束:一个线程等待多个线程执行完毕
经常网购的小伙伴对拼夕夕肯定不陌生吧,而拼夕夕有个特点,只有在商品拼团成功后,商家才会发货,如果一直没能成功,那你的订单会一直处于等待的状态;此外,游乐园里的过山车,一次可以坐10个人,商家为了节约成本,一般都是等待人满才会发车,这些都是一等多的生活场景。
- 控制子流程开始:多个线程等待一个线程执行完毕
这种场景也是非常常见,比如我们以前参加运动会时,100米短跑时都会先抬起优秀的翘臀,等待裁判员发出指令后才能起跑,只要裁判不发指令,所有运动员都要进行等待。此外还有一种场景离我们也是十分近—压测,压测也是为了模拟多个请求同时访问某个接口,预测出其支持最大压力程度。不知道有没有小伙伴遇到服务上线后,因为流量激增服务被干掉的情况,哎,不说了,都是泪。
二、CountDownLatch常见方法
CountDownLatch主要是通过AQS的共享锁机制实现的,内部逻辑比较简单,它的核心属性只有一个sync,这个后续会AQS会详细介绍
private final Sync sync;
复制代码
现在我们只要掌握以下4个属性在两种场景下的作用即可:
- CountDownLatch(int count): 类中唯一的构造器,
count
为倒数的次数; - await(): 调用该方法的线程会被挂起,直到
count
为0时才会被唤醒; - countDown(): 将count执行减一操作,当count为0时,等待中的线程会被唤醒;
- getCount(): 获取latch数量。
三、CountDownLatch代码演示
- 一等多场景
我们开发软件的目的是上线供用户使用,那再上线前肯定需要对系统进行全面测试,而很多模块间并没有依赖关系,因此可以同时进行测试,等待所以模块测试完毕,才能进行正式上线。
本例中假设有五个模块需要测试,并且模块测试可以同步进行,当每个模块测试完毕后会调用countDown
方法,执行减一操作,等到count
为0时,调用了await()
方法的主线程从挂起状态被激活,继续执行。
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
//创建5个线程,分别执行不通模块测试
for (int i = 1; i <= 5; i++) {
final int num = i;
Runnable runnable = () -> {
try {
//模拟测试耗时
Thread.sleep((long) (Math.random()*5000));
System.out.println("模块:"+ num +"测试完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//测试完毕后执行减一操作
latch.countDown();
}
};
service.submit(runnable);
}
System.out.println("等待模块检测...");
//主线程等待,直到count为0
latch.await();
System.out.println("所有模块测试完毕,开始上线");
}
复制代码
运行结果,当所有模块检测完毕后才开始上线:
等待模块检测...
模块:2测试完毕
模块:5测试完毕
模块:4测试完毕
模块:3测试完毕
模块:1测试完毕
所有模块测试完毕,开始上线
复制代码
如果小伙伴还是觉得很绕,这里画个图就基本明白了。
- 初始化
count
为5; - 主线程执行
await()
进入等待状态; - 各个模块调用
countDown()
方法对count
减一,并且线程不会进入休眠,而是继续执行自身逻辑; - 当
count
变成 0 后,主线程被唤醒。
- 多等一操作
这里使用运动会赛跑的栗子:在运动会上,所有运动员会先进行准备动作,只有等到裁判员发出信号,所有人才能开始比赛。
与之前的例子主要区别在于:CountDownLatch
初始变量count
为1,然后所有线程调用await
方法进行等待,直到主线程调用countDown
方法将count
值减为1,此时所有线程同时被唤醒,开始执行自身逻辑。
public static void main(String[] args) throws InterruptedException {
//重点:这里初始化为1
CountDownLatch begin = new CountDownLatch(1);
ExecutorService service = Executors.newFixedThreadPool(5);
//创建5个线程
for (int i = 1; i <= 5; i++) {
final int num = i;
Runnable runnable = () -> {
try {
System.out.println("运动员:"+ num +"等待起跑指令");
//所有运动员等待指令
begin.await();
System.out.println("运动员:"+ num +"开始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
service.submit(runnable);
}
Thread.sleep(1000);
//裁判员发出起步指令
begin.countDown();
System.out.println("发令完毕");
}
复制代码
运行结果,当发令法币后所有运动员才开始起跑:
运动员:1等待起跑指令
运动员:3等待起跑指令
运动员:4等待起跑指令
运动员:2等待起跑指令
运动员:5等待起跑指令
发令完毕
运动员:3开始跑步
运动员:4开始跑步
运动员:1开始跑步
运动员:5开始跑步
运动员:2开始跑步
复制代码
总结
以上就是CountDownLatch
的全部内容,相对来说比较容易掌握,理解难度不高,结合具体应用场景选择两种方式来使用,今天的文章就到这里咯,下一期唠唠其他好用的工具类吧,好好敲一敲,轻松搞定并发编程。
点关注,防走丢
以上就是本期全部内容,如有纰漏之处,请留言指教,非常感谢。我是花GieGie ,有问题大家随时留言讨论 ,我们下期见?。
文章持续更新,可以微信搜一搜 Java开发零到壹 第一时间阅读,并且可以获取面试资料学习视频等,有兴趣的小伙伴欢迎关注,一起学习,一起哈??。
原创不易,你怎忍心白嫖,如果你觉得这篇文章对你有点用的话,感谢老铁为本文点个赞、评论或转发一下
,因为这将是我输出更多优质文章的动力,感谢!