铂金07:整齐划一-CountDownLatch如何协调多线程的开始和结束
欢迎来到《并发王者课》,本文是该系列文章中的第20篇。
在上一篇文章中,我们介绍了Condition的用法。在本文中,将为你介绍CountDownLatch的用法。CountDownLatch是JUC中的一款常用工具类,当你在编写多线程代码时,如果你需要协调多个线程的开始和结束动作时,它可能会是你的不错选择。
一、CountDownLatch适用的两个典型应用场景
场景1. 协调子线程结束动作:等待所有子线程运行结束
对于资深的王者来说,下面这幅图一定再熟悉不过了。在王者开局匹配队友时,所有的玩家都必须进行确认操作,只有全部确认后才可以进入游戏,否则将进行重新匹配。如果我们把各玩家看作是子线程的话,那么就需要各子线程完成确认动作,游戏才能继续。其实,此类场景不止于王者,在生活中类似的场景还有很多。比如,所有乘客登机后飞机才能关舱门,除非超时后他们抛弃了你。

对于上图所示的玩家确认界面,如果用多线程模拟的话,那么它应该是下面的样子:

主线程创建了5个子线程,各子任务执行确认动作,期间主线程进入等待状态,直到各子线程的任务均已经完成,主线程恢复继续执行,也就是游戏继续。而如果其中某个玩家超时未执行确认的话,那么主线程将结束本次匹配,重新开始新一轮的匹配。
这个场景,就是CountDownLatch适用的第一个经典场景。
场景2. 协调子线程开始动作:统一各线程动作开始的时机
这个场景的例子也十分常见。比如,田径场上,各选手各就各位等待发令枪。在发令枪响之前,选手只能原地就位,否则就是违规。如果从多线程的角度看,这恰似你创建了一些多线程,但是你需要统一管理它们的任务开始时间。因为,如果你不对此做干预的话,线程调用start()之后的具体时间是不确定的,这个知识点我们早在青铜系列文章中就已经讲过。

在王者中也有类似的场景,游戏开始时,各玩家的初始状态必须一致。总不能,你刚降生,对方已经打到你家门口了。

上述的两个场景的问题,正是CountDownLatch所要解决的问题。理解了这两个问题,你也就理解了CountDownLatch存在的价值。
二、Java中的CountDownLatch设计
JUC中CountDownLatch的实现,是以类的形式存在,而不是接口,你可以直接拿过来使用。并且,它的实现还很简单。在数据结构上,CountDownLatch基于一个同步器实现,你可以看它的final Sync sync变量。
而在构造函数上,CountDownLatch有且只有CountDownLatch(int count)一个构造器,并且你需要指定数量,并且你不得在中途修改它,这点务必牢记!
核心函数
await():等待latch降为0;boolean await(long timeout, TimeUnit unit):等待latch降为0,但是可以设置超时时间。比如有玩家超时未确认,那就重新匹配,总不能为了某个玩家等到天荒地老吧。countDown():latch数量减1;getCount():获取当前的latch数量。
从CountDownLatch的方法上看,还是比较简单易懂的,重点要理解await()和countDown().
三、CountDownLatch如何解决场景问题
接下来,我们将第一小节的两个场景问题用代码实现一遍,让你对CountDownLatch的用法有个直观的理解。
场景1. CountDownLatch实现对各子线程的等待
创建大乔、兰陵王、安其拉、哪吒和铠等五个玩家,主线程必须在他们都完成确认后,才可以继续运行。
在这段代码中,new CountDownLatch(5)用户创建初始的latch数量,各玩家通过countDownLatch.countDown()完成状态确认。
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
Thread 大乔 = new Thread(countDownLatch::countDown);
Thread 兰陵王 = new Thread(countDownLatch::countDown);
Thread 安其拉 = new Thread(countDownLatch::countDown);
Thread 哪吒 = new Thread(countDownLatch::countDown);
Thread 铠 = new Thread(() -> {
try {
// 稍等,上个卫生间,马上到...
Thread.sleep(1500);
countDownLatch.countDown();
} catch (InterruptedException ignored) {}
});
大乔.start();
兰陵王.start();
安其拉.start();
哪吒.start();
铠.start();
countDownLatch.await();
System.out.println("所有玩家已经就位!");
}
场景2. CountDownLatch实现对多线程的统一管理
在这个场景中,我们仍然用五个线程代表大乔、兰陵王、安其拉、哪吒和铠等五个玩家。需要注意的是,各玩家虽然都调用了start()线程,但是它们在运行时都在等待countDownLatch的信号,在信号未收到前,它们不会往下执行。
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread 大乔 = new Thread(() -> waitToFight(countDownLatch));
Thread 兰陵王 = new Thread(() -> waitToFight(countDownLatch));
Thread 安其拉 = new Thread(() -> waitToFight(countDownLatch));
Thread 哪吒 = new Thread(() -> waitToFight(countDownLatch));
Thread 铠 = new Thread(() -> waitToFight(countDownLatch));
大乔.start();
兰陵王.start();
安其拉.start();
哪吒.start();
铠.start();
Thread.sleep(1000);
countDownLatch.countDown();
System.out.println("敌方还有5秒达到战场,全军出击!");
}
private static void waitToFight(CountDownLatch countDownLatch) {
try {
countDownLatch.await(); // 在此等待信号再继续
System.out.println("收到,发起进攻!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果如下,各玩家在收到出击信号发起了进攻:
敌方还有5秒达到战场,全军出击!
收到,发起进攻!
收到,发起进攻!
收到,发起进攻!
收到,发起进攻!
收到,发起进攻!
Process finished with exit code 0
小结
以上就是关于CountDownLatch的全部内容。总体上,CountDownLatch比较简单且易于理解。在学习时,先了解其设计意图,再写个Demo基本就能流畅掌握。
正文到此结束,恭喜你又上了一颗星
夫子的试炼
- 编写代码体验CountDownLatch用法。
延伸阅读与参考资料
最新修订及更好阅读体验
关于作者
关注【技术八点半】,及时获取文章更新。传递有品质的技术文章,记录平凡人的成长故事,偶尔也聊聊生活和理想。早晨8:30推送作者品质原创,晚上20:30推送行业深度好文。
如果本文对你有帮助,欢迎点赞、关注、监督,我们一起从青铜到王者。
铂金07:整齐划一-CountDownLatch如何协调多线程的开始和结束的更多相关文章
- Python 多线程的程序不结束多进程的程序不结束的区别
import time from threading import Thread from multiprocessing import Process #守护进程:主进程代码执行运行结束,守护进程随 ...
- 并发王者课-铂金8:峡谷幽会-看CyclicBarrier如何跨越重峦叠嶂
欢迎来到<并发王者课>,本文是该系列文章中的第21篇,铂金中的第8篇. 在上一篇文章中,我们介绍了CountDownLatch的用法.在协调多线程的开始和结束时,CountDownLatc ...
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...
- 多线程之倒计时器CountDownLatch和循环栅栏CyclicBarrier
1.倒计时器CountDownLatch CountDownLatch是一个多线程控制工具类.通常用来控制线程等待,它可以让一个线程一直等待知道计时结束才开始执行 构造函数: public Count ...
- Java中使用CountDownLatch进行多线程同步
CountDownLatch介绍 在前面的Java学习笔记中,总结了Java中进行多线程同步的几个方法: 1.synchronized关键字进行同步. 2.Lock锁接口及其实现类ReentrantL ...
- Java多线程系列
一.参考文献 1.:Java多线程系列目录 (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式 03. ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- Java多线程系列目录(共43篇)
最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...
随机推荐
- Form-OCR & CSDNAPP初体验
项目 内容 课程:北航2020春软件工程 博客园班级博客 作业:阅读并撰写博客回答问题 软件案例分析 我在这个课程的目标是 全面地评价一个软件 这个作业在哪个具体方面帮助我实现目标 明确软件开发过程中 ...
- 探索GaussDB(DWS)的过程化SQL语言能力
摘要:在当前GaussDB(DWS)的能力中主要支持两种过程化SQL语言,即基于PostgreSQL的PL/pgSQL以及基于Oracle的PL/SQL.本篇文章我们通过匿名块,函数,存储过程向大家介 ...
- 『动善时』JMeter基础 — 21、HTTP Cookie管理器的使用
目录 1.在HTTP信息头管理器组件中添加Cookie信息 (1)测试计划内包含的元件 (2)请求取样器内容 (3)HTTP信息头管理器内容 (4)查看结果 2.使用HTTP Cookie管理器组件来 ...
- webpack解析(1)
webpack是为现代js程序准备的静态模块打包工具 一:关于对webpack的理解 可以将其认为是一个电脑主板,由于使用js作为源码,因而其可以默认编译js代码(别种类型的文件可以依靠loaders ...
- Linux信号与golang中的捕获处理
什么是信号 在计算机科学中,信号是Unix.类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式.它是一种异步的通知机制,用来提醒进程一个事件已经发生. 当一个信号发送给一个进程, ...
- [bug] Failed building wheel for xxx
参考 https://blog.csdn.net/pengzhisen123/article/details/79049834 https://www.lfd.uci.edu/~gohlke/pyth ...
- [BD] Storm
什么是实时计算 离线计算:批处理,代表MapReduce.Spark Core,采集数据Sqoop.Flume 实时计算:源源不断,代表Storm等,采集数据Flume 框架 Apache Storm ...
- [Qt] 事件机制(四)
滚轮事件:滚动滚轮实现窗口大小缩放 widget.h中增加: protected: void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void ...
- 选择“保留window设置、个人文件及应用”或者“升级安装windows并保留文件设置和应用程序”的 处理干净以后用ghost备份
个人经验 第一次装好以后 把所有常用软件什么的 还有系统的更新全部装好 删去乱七八糟的临时文件啊什么的 处理干净以后用ghost备份下次需要重装直接从ghost镜像恢复 然后更新软件 打补丁 再备份 ...
- Linux进阶之软件管理
本节内容 一.rpm:管理linux软件程序的 特点:安装方便 不能解决依赖关系 1.安装软件: -i: -v: -h: rpm -ivh 包名 2.卸载软件 -e: 清楚 rpm -e 程序名 3. ...