文章目录
💯 核心作用:
1、同步辅助类: CountDownLatch是一个同步辅助类,用于在完成一组正在其他线程中执行的操作之前,允许一个或多个线程等待。
2、计数器: 它维护一个计数器,初始化时设定计数,调用countDown()方法会减少计数器,而await()方法会阻塞,直到计数器为零。
3、用途: 用于控制一组线程等待某个事件发生后再全部同时继续执行。
正文开始

凌晨 4 点救了我上线的 “线程点名器”:CountDownLatch 的故事
零、引入
凌晨两点的运维室,你盯着监控屏手都在抖 —— 明天要上线的电商系统,压测时突然卡在 “支付模块初始化”,日志刷了满屏 “等待子任务完成”,而距离最终上线只剩 4 小时。更要命的是,你上周刚跟领导拍胸脯说 “线程同步这块稳得一批”,现在脸疼得像被虚拟机蓝屏弹窗抽了耳光。

就在你准备打开脉脉更新 “求内推,擅长背锅” 时,老架构师王哥叼着没点燃的烟凑过来:“慌啥?不就是没给线程装‘点名器’嘛,CountDownLatch 了解下?

一、CountDownLatch:本质是个 “线程版点名器”
王哥把你的代码拉到面前,指着其中一段笑出了抬头纹:“你看你这初始化逻辑,主线程喊了句‘大家开始干活’,就自顾自等结果了 —— 小红的缓存预热还没跑完,小刚的数据库连接池还没初始化,主线程就急着进下一步,不卡才怪。”

他边说边在键盘上敲了几行代码,像给混乱的工地立了块警示牌:
package cn.tcmeta.coundownlatch;
import java.util.concurrent.CountDownLatch;
/**
* @author: laoren
* @description: 经典使用示例
* @version: 1.0.0
*/
public class CountDownLatchSample {
static void main() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
// 任务1:缓存预热(小红负责)
new Thread(() -> {
try {
// 执行缓存预热逻辑();
System.out.println("小红:缓存搞定!");
} finally {
// 完成一个任务,就在点名册上划掉一个名字
latch.countDown();
}
}).start();
// 任务2:数据库连接池初始化(小刚负责)
new Thread(() -> {
try {
// 执行数据库连接池初始化();
System.out.println("小刚:数据库搞定!");
} finally {
latch.countDown();
}
}).start();
// 任务3:支付接口联调(小明负责)
new Thread(() -> {
try {
// 执行支付接口联调();
System.out.println("小明:接口搞定!");
} finally {
latch.countDown();
}
}).start();
latch.await();
System.out.println("所有任务完成,开始启动服务!");
}
}

你盯着代码突然反应过来:“这不就是开会时‘等所有人到齐再开始’吗?”王哥弹了你个脑瓜崩:“总算开窍了!CountDownLatch 就干三件事:
- 初始化时说清‘要等多少个线程’(比如 3 个任务就传 3);
- 每个线程干完活喊一声countDown(),相当于‘报告,我完事了’;
- 主线程喊await(),意思是‘我就在这等,没到齐谁也别想走’。”
更绝的是,王哥补充道:“哪怕有线程抛异常,finally里的countDown()也能保证‘点名册划得完’,不会让主线程在那当一辈子望夫石 —— 这比你之前用Thread.join()靠谱多了,join()要是线程崩了,主线程能等成化石。”
二、这玩意儿除了救上线,还能干嘛?
压测很快通过,你终于能喘口气,王哥却没停下:“别以为这玩意儿只能救急,日常开发里用处多着呢,比如你上次吐槽的‘压测统计不准’问题。”
你瞬间想起上个月的糗事:压测时模拟 1000 个用户下单,结果主线程没等所有请求跑完就统计结果,算出的 “平均响应时间” 比实际快了一半,被测试小姐姐笑 “数据造假”。
“那时候要是用 CountDownLatch,就不会翻车了。” 王哥随手画了个场景:
- 先建一个计数 1000 的 Latch;
- 每个模拟用户的线程跑完请求就countDown();
- 主线程await()到计数为 0,再计算响应时间总和 —— 数据准得像用了卡尺量。
他还补了个程序员才懂的段子:“记住别跟 CyclicBarrier 搞混啊!
- CountDownLatch 是‘老板等所有员工下班锁门’,锁完门这事儿就结束了;
- CyclicBarrier 是‘团建等所有人到齐再发车’,到了景点还能等下一波人再发车 —— 你要是在循环里用 CountDownLatch,就像锁了门还想再用这把锁,纯属找骂。”
三、总结:记住这 3 点,别再踩坑
眼看天快亮了,王哥把烟揣回兜里:“CountDownLatch 不难,核心就 3 个点,记牢了别再让我半夜来救场:
- 计数只能减不能加:初始化时定好数,countDown()一次减 1,到 0 就没用了,想重置找 CyclicBarrier;
- await()会阻塞主线程:别在 UI 线程里用,不然页面能卡成 PPT,后台线程用才合适;
- 异常一定要处理:把countDown()放finally里,不然线程崩了没减计数,主线程能等得 CPU 都凉了。”
彩蛋时间:王哥临走前偷偷跟你说:“上次小李更离谱,在循环里每次都new CountDownLatch(3),结果线程越跑越多,JVM 直接报 OOM—— 你可别学他,代码 review 时能被笑到退休。”
四、最后说句实在的
其实 Java 里的线程同步工具,都像咱们干活的 “规矩”—— 没规矩时线程乱成一锅粥,有了 CountDownLatch 这种 “点名器”,才能让多线程按节奏来。
要是你今天搞懂了,别忘了点赞让更多人避坑,也赶紧关注我 —— 下次咱们扒一扒 “线程池里那些让你加班到哭的坑”,再教你怎么用 Semaphore 解决 “并发抢资源” 的问题。
对了,把这篇分享给你那总写 “裸奔线程” 的同事,下次上线你们就能一起准点下班,不用再在运维室啃凉包子啦!


636

被折叠的 条评论
为什么被折叠?



