当前位置: 首页 > news >正文

Java并发利器:CyclicBarrier深度解析

CyclicBarrier是 Java 并发包中一个可重用的同步辅助工具,用于让一组固定数量的线程互相等待,直到所有线程都到达某个“屏障点”(barrier point),然后一起继续执行。它的名字中的“Cyclic”(循环)正是因为它可以在一次使用后重置并重复使用

下面从核心思想、内部机制、关键特性、与 CountDownLatch 的区别、内存语义五个方面帮你彻底理解。


一、核心思想:多线程“齐步走”

想象一个班级做操:

  • 老师喊:“所有人到操场集合!”
  • 学生 A、B、C……陆续到达,但必须等最后一个同学到齐,才能开始做操。
  • 做完一轮后,老师又喊:“再来一轮!”,大家再次集合 →这就是“循环”

在程序中:

  • 每个线程执行一部分工作;
  • 然后调用barrier.await()等待;
  • 所有 N 个线程都调用了await(),屏障“放行”,所有线程继续;
  • 下次还能再用同一个CyclicBarrier

典型场景:并行迭代计算(如 Jacobi 迭代)、多阶段任务同步。


二、内部实现机制

1. 核心组件

privatefinalReentrantLocklock=newReentrantLock();privatefinalConditiontrip=lock.newCondition();// 等待/唤醒的条件privatefinalintparties;// 总线程数(固定)privateintcount;// 当前轮次剩余未到达的线程数privateGenerationgeneration;// 表示当前“代”(一轮)privatefinalRunnablebarrierCommand;// 屏障动作(可选)

2.Generation:区分“轮次”

  • 每次屏障触发(tripped)或重置(reset),就创建一个新的Generation对象。
  • 所有等待的线程都关联到同一个 generation
  • 如果某轮被中断(如超时、异常),该 generation 被标记为broken = true
  • 新一轮开始时,generation指向新对象,旧的被废弃。

💡 作用:防止“跨轮次”的线程互相干扰(比如上一轮的线程误唤醒下一轮的)。

3.dowait():核心等待逻辑

intindex=--count;// 倒计数if(index==0){// 最后一个线程到达!执行 barrierCommand(如果有);nextGeneration();// 重置 count,新建 generation,signalAll()}else{// 不是最后一个,进入等待trip.await();// 或 awaitNanos()}
关键行为:
  • 最后一个到达的线程负责:
    • 执行barrierCommand(如合并结果);
    • 调用trip.signalAll()唤醒所有等待者;
    • 创建新一代(nextGeneration())。
  • 其他线程:阻塞在trip.await(),直到被唤醒。

三、关键特性

1.All-or-Nothing(全有或全无)

  • 如果任何一个线程在等待时被中断、超时、或调用reset()
  • 那么所有等待的线程都会抛出BrokenBarrierException
  • 并且 barrier 进入broken 状态

这保证了“要么全部通过,要么全部失败”,避免部分线程卡住。

2.Barrier Action(屏障动作)

  • 可在构造时传入一个Runnable
    newCyclicBarrier(N,()->mergeResults());
  • 该动作由最后一个到达的线程执行,在唤醒其他人之前
  • 常用于:汇总本轮结果、更新全局状态。

3.返回到达序号

intarrivalIndex=barrier.await();
  • 返回值:parties - 1表示第一个到达,0表示最后一个。
  • 可用于:指定某个线程(如if (await() == 0))执行日志、清理等操作。

4.可重置(reset)

barrier.reset();// 立即打破当前轮次,开启新轮次
  • 所有当前等待的线程会收到BrokenBarrierException
  • 之后可以重新开始新一轮同步。

四、与CountDownLatch的本质区别

特性CyclicBarrierCountDownLatch
用途多线程互相等待(N 对 N)一个/多个线程等其他线程完成(1 对 N 或 N 对 1)
可重用✅ 是(cyclic)❌ 否(one-shot)
触发者所有参与线程自己调用await()其他线程调用countDown()
屏障动作✅ 支持(最后一个线程执行)❌ 不支持
失败模型All-or-nothing(全失败)单独失败不影响他人
底层实现ReentrantLock + ConditionAQS 共享模式

📌简单记

  • CountDownLatch“我等你们干完”(协调者模式)。
  • CyclicBarrier“我们互相等,一起走”(协作模式)。

五、内存可见性(Memory Consistency)

Java 内存模型保证以下happens-before关系:

线程 A 在await()之前的操作
happens-before
屏障动作(barrier action)的执行
happens-before
其他线程从await()返回后的操作

这意味着:

  • 所有线程在await()前写入的共享数据,
  • 在屏障动作中可见,
  • 并且在其他线程继续执行后也可见。

✅ 无需额外同步!


六、使用示例回顾(你的代码)

// N 个 worker 线程并行处理矩阵行barrier=newCyclicBarrier(N,()->mergeRows(...));Worker.run(){while(!done()){processRow(myRow);barrier.await();// 等待所有行处理完}}
  • 每轮:所有线程处理一行 → 等待 → 合并结果 → 下一轮。
  • 如果mergeRows()发现解已找到,done()返回 true,线程退出。
  • 完美体现“循环同步 + 屏障动作”。

七、总结:一句话理解 CyclicBarrier

它是一个可重复使用的“集合点”:N 个线程各自完成任务后在此汇合,全部到齐才一起继续;支持在汇合时执行一个汇总操作,并且任何一人掉队(中断/超时)则全员失败。

它的设计精妙之处在于:

  • Generation隔离不同轮次;
  • ReentrantLock + Condition实现高效等待/唤醒;
  • 提供强一致性语义和灵活的扩展点(barrier action)。

这也是 Doug Lea 并发设计的经典之作。

http://www.jsqmd.com/news/200809/

相关文章:

  • Mybatis-plus自动填充字段
  • 深入解析AI Agent五件套:从感知到学习的完整指南【必收藏】
  • 【必学收藏】大模型架构深度解析:一文读懂自注意力机制原理与代码实现
  • 【QWen1.5】使用AutoDL多卡对QWen1.5-7B模型进行lora微调
  • 原创大规模无人机检测数据集:11998张高质量图像,支持YOLOv8、COCO、TensorFlow多格式训练,涵盖飞机、无人机、直升机三大目标类别
  • 为什么大模型如此强大我们还要微调?程序员必收藏的微调详解
  • 网页自动翻页工具(执行PageDown)
  • 在 IntelliJ IDEA 中使用 JUnit 进行单元测试 - 详解
  • 【值得收藏】MCP协议入门到实战:大模型与外部系统交互的通用桥梁,附代码与学习资源
  • 收藏必备!从零构建AI Agent:知识库、工作流与Prompt工程实战指南
  • 从入门到精通:企业级RAG系统实战指南,收藏级RAG开发全流程解析
  • CSS极坐标的实例代码
  • 数学总结
  • 2025 年度总结:在坚持与突破中前行
  • 你的网站SSL证书又要过期了?这个工具能让你永久告别焦虑
  • 【必收藏】法律大模型实战:从文档到知识图谱的RAG系统构建全攻略
  • 2026爆火6款免费AI论文生成器:1小时初稿全学科覆盖!
  • RAG全栈学习笔记-Graph RAG
  • 【珍藏】从零掌握大模型检索增强技术:RAG到GraphRAG的完整指南
  • Ftp服务部署
  • 实用指南:Node.js:从浏览器到服务器的JS革命
  • 【必学收藏】MoE架构深度解析:大模型高效训练的核心技术与实战应用
  • 本地知识库:你的数据安全守护者
  • 【必学收藏】从入门到精通:Prompt工程的演变、价值与实战技巧
  • 智能学习资源管理平台 - Beta冲刺总结
  • 牛批了,windows系统神器,一键搞定
  • Linux 中 awk 利用外部文件传入数字变量,利用循坏提取特定列
  • 大模型应用架构实战指南:六大核心层解析与最佳实践,程序员必读收藏
  • 【参数优化】基于粒子群算法实现GaN HEMT小信号模型器件拟合参数优化附matlab代码
  • 2026爆火6款AI论文工具:一键生成初稿,写论文从未如此简单!