原文来自于:[zha-ge.cn/java/75http...
Java CyclicBarrier 详解:原理、使用方式与应用场景
说起来多线程,真的是一把双刃剑,玩的爽的时候效率飞起,踩坑的时候能让你想砸键盘。那天被产品催着搞个并发汇总批量处理,"必须所有线程都处理完才能出最终结果哈!"老板一句话,我心里mmp冒了三句,然后想到了老朋友------CyclicBarrier。
那些年被读写锁捣鼓晕了
先给没见过 CyclicBarrier 的朋友科普一句,它的意思其实就是"循环栅栏",就像个闸门------得等所有小伙伴都到了门口,门一开大家再齐头并进。所以它适用于那种"大家一起做点事,等大家都干完才能继续"的场合。
一开始你可能想:"我用 CountDownLatch 不就行?"确实,俩有点像。但 CyclicBarrier 能用多轮,Latch 一次性的,完事就没法再用了。
突发奇想用 CyclicBarrier 干票大的
我的场景是这样:假如有三个子线程,必须等它们都到齐,聚在一起后,才能合体出最终报告。伪代码像这样:
java
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("大家一起出发!"));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 各自干各自的活
doSomething();
try {
barrier.await(); // 等到小伙伴都到了
} catch (Exception e) {
// 啊又爆炸了!
}
// 继续后续操作
}).start();
}
哪怕你有 30 个线程也照样安排,只要前面数字一样,线程就小学生排队似的,你来我来大家一起闯关。
踩坑瞬间
别以为 CyclicBarrier 就完美了哈!我第一次用的时候,线程里有个"不小心"抛了个 Exception,结果整个 barrier.await 就等到天荒地老。啥原因?原来只要有一个 thread 异常退出了,其他的就都被冻那了------barrier 一直等不到齐活。
总结几个坑点:
- 有一个线程异常退出,整个 barrier 卡死,剩下的线程永远等不来 release。
- 重复利用 CyclicBarrier 时,记住 barrier.reset() 是把大家拉回起跑线,但正在等待的那帮线程会直接收到异常(BrokenBarrierException),得做好异常处理。
- 提前触发 barrier 任务(那第二个参数 Runnable),如果任务失败也会导致大家全都抛异常。
举个踩坑代码片段,全靠经验教训:
java
try {
barrier.await(); // 这里如果线程提前崩了,剩下的人就直接 BrokenBarrierException了
} catch (BrokenBarrierException | InterruptedException e) {
// 做好善后,不然你得加班写年终总结......
}
CyclicBarrier 最妙的感觉
用完 CyclicBarrier,我体会到最美的一刻就是三个线程像打卡上班,一起蹲在门口,等班长(barrier任务)发指令,然后啪,一起冲。特别适合:
- 多个线程数据计算,最后聚合
- 分段下载,等分块都好了再合成
- 测试场景下模拟"大部队"同时开火
别的场景你也可以"想歪用一下",比如刷队伍小游戏(拼手速),别愣着,场景绝对取之不尽。
经验启示
又到了填鸡汤瓶的时间------自己亲身试过后,才发现:
- CyclicBarrier 爱凑数儿,线程数对不上就没得玩
- 一定要处理异常,尤其是 BrokenBarrierException
- 它循环可重用,比 CountDownLatch 强在玩多轮赛
- barrier内部有个"小小黑屋"(generation),重置和异常都会让大家被踢出来,记得写好兜底
最后送你一张我的偷懒手册:
关键点 | 记忆口诀 |
---|---|
对线程数有要求 | 人没到齐,门不开 |
适合多轮齐步走 | 一拨儿走完还能再来一拨 |
异常要特别留意 | 讹出一个,全员搁浅 |
溜了溜了......
写的累,但 CyclicBarrier 用顺了是真香。前提是你真知道自己要的啥,别上线了一堆线程尬等。对了,如果你也踩过啥多线程的坑,评论里一起唠嗑,帮我打打气,下次再写点更野的故事!