文章目录
CyclicBarrier的定义及与
CountDownLatch的区别:
1、定义: CyclicBarrier是一个同步辅助类,它允许一组线程相互等待,直到所有线程都达到了公共屏障点(Barrier)。
2、可重用性: CyclicBarrier与CountDownLatch的一个主要区别是它可以重用,当所有等待线程都释放后,可以重置屏障再次使用。
3、用途: CyclicBarrier通常用于一组线程互相等待至某个状态之后再全部同时执行。
4、动作执行: CyclicBarrier可以在所有线程到达屏障时优先执行一个预定义的动作。
正文开始:

🚀 团建翻车后,我终于懂了 CyclicBarrier:它和 CountDownLatch 的区别太绝了!
零、引入
上回你用 CountDownLatch 救了上线,在公司吹了半个月 “线程同步大神”,结果这周公司团建,你主动揽下 “组织发车” 的活儿,却把自己坑成了全公司的笑柄 ——咋回事呢?
行政小姐姐让你负责 3 辆大巴,每车满 20 人就发车去景点。你拍胸脯说 “包在我身上,用 CountDownLatch 秒搞定”,结果第一辆车顺利发车,第二辆车刚凑够人,你手里的 “点名器” 却失灵了,任凭你怎么点,CountDownLatch 都纹丝不动,最后 30 多号人堵在停车场骂骂咧咧,你站在太阳下,脸比 JVM 内存溢出时的日志还红。
就在你准备在群里发 “辞职申请.pdf” 时,王哥叼着冰可乐走过来:“小伙子,用 CountDownLatch 干循环集合的活儿,跟用一次性筷子夹火锅似的 —— 纯属找罪受!这事儿得用 CyclicBarrier 啊!”

一、CyclicBarrier:可循环使用的 “团建集合哨”
王哥一把夺过你手里的平板(你居然想用 Excel 统计人数,也是没谁了),边演示边笑:“CountDownLatch 是‘老板等所有员工下班锁门’,锁完这把锁就废了;但 CyclicBarrier 是‘导游等游客集合发车’,到了景点还能再吹哨集合去下一站 —— 这玩意儿能循环用,懂?”
他随手敲了段代码,完美复刻团建场景:
package cn.tcmeta.cyclicbarrier;
import java.util.concurrent.CyclicBarrier;
/**
* @author: laoren
* @description: CyclicBarrier, 循环屏障
* @version: 1.0.0
*/
public class CyclicBarrierSample {
static void main() {
CyclicBarrier barrier = new CyclicBarrier(20, () -> {
System.out.println("📢📢📢📢📢 导游: 人齐了, 发车去下一站!!");
});
// 模拟50个员工陆续到达
for (int i = 0; i < 50; i++) {
int threadId = i;
new Thread(()->{
try {
System.out.println("😂员工 " + threadId + " 到达");
// 等待其他人集合,凑够20人就触发上面的“发车”动作
barrier.await();
System.out.println("✔️员工 " + threadId + " 坐上大巴车, 出发喽 ~");
}catch (Exception e){
e.printStackTrace();
}
}).start();
}
}
}
D:\java25\bin\java.exe --enable-preview "-javaagent:D:\Program Files\JetBrains\IntelliJ
😂员工 4 到达
😂员工 2 到达
😂员工 3 到达
...
😂员工 44 到达
😂员工 34 到达
📢📢📢📢📢 导游: 人齐了, 发车去下一站!!
✔️员工 8 坐上大巴车, 出发喽 ~
😂员工 28 到达
✔️员工 37 坐上大巴车, 出发喽 ~
😂员工 38 到达
✔️员工 13 坐上大巴车, 出发喽 ~
你盯着代码恍然大悟:“所以它叫 Cyclic(循环)Barrier(屏障),就是因为能反复‘拦着’人,凑够数就放行,放行完还能再拦?”
“总算没笨到家!” 王哥弹了弹你的额头,“CyclicBarrier 就干两件核心事:
- 初始化时定好‘凑够多少人放行’(比如 20 人),还能加个‘凑够人后要干的事’(比如发车);
- 每个线程调用await(),就是‘我到齐了,等其他人’,直到所有线程都调用await(),屏障才会打开;
- 打开后屏障会自动重置,下次还能凑够 20 人再放行 —— 这就是它和 CountDownLatch 最大的区别:
- CountDownLatch 是‘一次性消耗品’,CyclicBarrier 是‘可重复利用的工具’。”
还补了个冷笑话:“你想想,CountDownLatch 像你双十一抢的一次性口罩,用一次就扔;CyclicBarrier 像你办公室的保温杯,天天用都没问题 —— 用错了,不是冻着就是尴尬着!”

二、什么时候用 CyclicBarrier?这些场景比 CountDownLatch 香
团建风波平息后,你好奇:“除了团建集合,这玩意儿在代码里还有啥用?”
王哥给你举了 3 个实战场景,每个都戳中程序员痛点:

2.1 分批处理海量数据(比单线程快 10 倍)
比如你要处理 100 万条用户数据,每条都要查数据库、算积分、写日志 —— 单线程跑要 2 小时,用 CyclicBarrier 分 10 批处理:
- 每批 10 万条数据,建一个计数 10 的 CyclicBarrier(10 个线程处理一批);
- 10 个线程都处理完自己的 1 万条,屏障打开,触发 “汇总该批结果” 的动作;
- 屏障自动重置,接着处理下一批 10 万条;
- 最后汇总 10 批结果,总耗时 20 分钟搞定,效率直接拉满。
“这场景下 CountDownLatch 就不行了,” 王哥吐槽,“你总不能每批都 new 一个 CountDownLatch 吧?跟你团建时每发一辆车就买一把新锁似的,不仅麻烦,还容易搞出内存泄漏 ——JVM 看了都想骂‘你是不是有病’。”
2.2 多线程协作完成 “分步任务”(比如游戏加载)
比如你做一个手游,加载界面需要 3 步:加载地图、加载角色、加载音效,这 3 步可以并行,但必须都完成才能进游戏;而且玩家死了复活后,还得重新加载这 3 步。
用 CyclicBarrier 就完美:
- 计数设为 3,每次复活都触发一次 “加载 3 步”;
- 3 个线程分别加载地图、角色、音效,加载完调用await();
- 3 个都完成,屏障打开,玩家进入游戏;
复活后,屏障已重置,重复上述流程 —— 比 CountDownLatch 省了无数个 “new 对象” 的操作。
2.3 压力测试中的 “同步并发”(比 CountDownLatch 更灵活)
比如你要模拟 1000 个用户同时下单(注意是 “同时”,不是 “陆续”),CountDownLatch 能等所有用户下单完统计结果,但没法保证 “同时开始下单”;而 CyclicBarrier 可以:
- 1000 个用户线程先调用await(),都等着;
- 等所有线程都准备就绪,屏障打开,1000 个下单请求同时发起;
- 后续还能再凑 1000 个用户,再发起一轮测试 —— 循环利用,爽得飞起。
这里插个段子:“很多程序员分不清这俩,就像分不清‘奶茶三分糖’和‘奶茶少糖’—— 看着像一回事,实际差远了!CountDownLatch 是‘我等你们都干完我再干’,CyclicBarrier 是‘我们都干完了,一起干下一件’;CountDownLatch 是‘老板监工’,CyclicBarrier 是‘工友抱团’!”
三、CountDownLatch vs CyclicBarrier:4 个核心区别,记牢不踩坑

为了让你彻底分清,王哥画了个 “灵魂对比表”,还配了段子:
| 特性 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 核心功能 | 主线程等多个子线程完成(单向等待) | 多个线程互相等,凑齐后一起执行(双向等待) |
| 是否可循环使用 | 否(一次性,计数到 0 失效) | 是(自动重置,可重复用) |
| 计数方向 | 初始化后只减不加 | 计数到 0 后自动重置为初始值 |
| 触发动作 | 无默认动作,计数到 0 直接放行 | 可指定 “凑齐后执行的 Runnable 任务” |
“再给你整个通俗的比喻,” 王哥说,“CountDownLatch 像高考:所有考生(子线程)考完,阅卷老师(主线程)才开始阅卷,考完这波就结束了;CyclicBarrier 像考研复试:所有考生(子线程)都到齐了,面试官才开始面试,这波面完,下一波考生还能接着用这个面试室 —— 这就是‘循环’的精髓!”
冷笑话预警:“为什么 CyclicBarrier 在循环场景里比 CountDownLatch 受欢迎?因为 CountDownLatch 是‘一次性筷子’,用一次就扔,环保部门(JVM)不喜欢;CyclicBarrier 是‘可降解餐具’,循环利用,还能减少内存垃圾 —— 程序员要懂环保,更要懂代码环保!”
四、总结:记住这 2 句口诀,再也不搞混

王哥喝了口可乐,总结道:“其实不用死记硬背,记住两句口诀就行:
- 要‘一次性等待多线程完成’—— 用 CountDownLatch(老板锁门);
- 要‘循环等待多线程凑齐,再一起干活’—— 用 CyclicBarrier(导游发车)。
另外再提醒 2 个坑,别踩:
- CyclicBarrier 的await()会抛 InterruptedException 和 BrokenBarrierException,前者是线程被打断,后者是有线程跑路(异常退出)导致屏障破裂,记得处理,不然会导致其他线程一直等;
- 别在单线程里调用await(),不然线程自己等自己,直接死锁 —— 上次小张就干过这事儿,代码跑了半天没反应,最后发现是自己跟自己‘集合’,被我们笑了一个月。”
彩蛋时间:王哥偷偷告诉你:“上次行政小姐姐学了 CyclicBarrier 后,现在公司团建不仅能分批发车,到景点还能分批集合去吃饭、分批集合去返程,效率高得一批,还特意给我送了杯奶茶 —— 你要是早点懂,也能在行政小姐姐面前露一手,不至于上次团建被骂‘组织能力不如 PPT’!”
五、最后说句实在的

Java 的线程同步工具,就像生活里的 “协作神器”—— 用对了省时间、少挨骂,用错了不仅自己加班,还得被同事笑话。
今天搞懂了 CyclicBarrier 和 CountDownLatch 的区别,别忘了点赞让更多人避坑,赶紧关注我 —— 下次咱们扒一扒 “Semaphore 怎么解决‘并发抢资源’的问题”,再教你怎么用 Phaser 搞定 “动态增减线程的同步”,让你彻底告别 “线程同步加班” 的噩梦。
对了,把这篇分享给你那总把 “CyclicBarrier” 念成 “循环栅栏” 还写错代码的同事,下次代码 review 时,你就能笑着指出他的错误,然后优雅地说:“兄弟,团建发车的道理,你还没懂啊?”



446

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



