像我们程序员呢,经常会去参加需求评审,一般来说,都是开发、测试、产品经理都到齐了才能开始的,会议的负责人是产品经理。这里就有「等」的问题,等人齐了,就开始开会,人没有齐,会议不会开。
在程序的世界里,这种「人齐了才能干活」的场景同样也是随处可见的。比如说,一个电商系统的商品聚合详情页,需要同时去调用商品基础信息服务、库存服务和优惠券服务。为了提高响应速度,可以开启3个并发线程去分别拉取数据。但最后拼装数据返回给前端时,主线程必须等这3个子任务全部完成,任何一个数据没拿到,页面都无法完整渲染。
那么,主程序怎么知道这3个线程到底有没有到齐呢?
这时,我们就需要一位会议主持人(主线程)和签到员(同步工具CountDownLatch)。
注意,签到员才是CountDownLatch。主线程不是,CountDownLatch是用来协调线程的,在这里就是要协调主线程和子线程(协同的线程)。
为了方便大家理解和做好映射,我列一个表格,把上面需求评审会的场景跟CountDownLatch映射起来。
| 需求会议中的场景 | Java中的代码逻辑 |
|---|---|
| 签到员拿出一张纸,上面写着应到3人 | CountDownLatch latch = new CountDownLatch(3); |
| 开发/测试/产品赶到会议室,向签到员报到 | 每个子线程完成任务后,调用latch.countDown()(计数器 -1) |
| 主持人问签到员:人齐了吗? 签到员说:还没,你先坐着等。 | 主线程调用latch.await(),且被阻塞挂起 |
| 当最后一个人报到,签到员看表:还差0人,人全齐了! | 计数器扣减到0 |
| 签到员叫醒主持人:可以开会了 | CountDownLatch唤醒被阻塞的主线程,await()方法返回,主线程继续往下执行 |
好,下面我们将上面的场景,用java 17来实现。
用代码模拟
对应到代码里:主持人就是主线程,签到员就是一个CountDownLatch,开发、测试、产品经理是三个参会线程。完整代码如下:
java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
public class MeetingDemo {
public static void main(String[] args) throws Exception {
// 签到表:应到3人,开发、测试、产品经理各一个
CountDownLatch latch = new CountDownLatch(3);
String[] roles = {"开发", "测试", "产品经理"};
// 三个参会人各起一个线程,从工位走到会议室
for (String role : roles) {
new Thread(() -> {
try {
// 走路耗时不一样,300到800毫秒不等
Thread.sleep(ThreadLocalRandom.current().nextInt(300, 800));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(role + "到了会议室");
// 人到就向签到员报到,签到表上的名字划掉一个
latch.countDown();
}).start();
}
// 主持人(主线程)在签到员这里等,人没齐就停着
latch.await();
System.out.println("人到齐了,会议开始");
}
}
运行结果如下:
text
产品经理到了会议室
开发到了会议室
测试到了会议室
人到齐了,会议开始
三个参会人各是一个线程,走路的耗时不一样,所以每遍输出里,到达的顺序都不一样。每个人到会议室后做的事都是一样的:向签到员报到,countDown()把签到表上的名字划掉一个。主持人(主线程)到了就await(),在签到员这里等,名字没划完就一直停着。
三个名字全部划掉,计数从3减到0,await()返回,对应的就是表格里的最后一行:签到员叫醒主持人,会议开始。
业务代码里使用CountDownLatch的场景
我这边在生产环境里,是经常使用CountDownLatch的,下面我列其中一个场景,批量翻译。
由于我这边一直是从事连锁店行业的,因此相关的业务场景,也是连锁店相关的。
我们当时的门店是开到全世界的,日本,美国,澳门,香港等,都在开店,因此相关的语言是做翻译的。
当运营点一下一键翻译,一批文案要调外部翻译服务,翻成多个语种,单次调用几百毫秒。因此就做并发翻译,主线程要等这批全部翻完,才把结果组装好返回到页面。
凡是主线程需要等多个子线程干完后,才能开始的,你就可以使用CountDownLatch这个同步工具。
这篇内容的主要目的,就是让你了解什么是CountDownLatch,至于实现原理的,后续的内容会写。
感谢您的阅读,希望这篇文章可以帮助到你。