cyclicbarrier 通过内置锁机制间接触发 happens-before 关系保障本轮同步的变量可见性;跨轮次需 volatile 或原子类。

CyclicBarrier 本身不直接提供变量可见性的语义保障,但它通过内置的锁机制和 await() 的同步行为,**间接触发 JMM 中的 happens-before 关系**,从而让线程间对共享变量的读写具备可见性。关键不在 Barrier 本身“保证”可见性,而在于它如何与监视器锁规则、volatile 语义协同工作。
await() 调用构成一次完整的锁同步点
CyclicBarrier 内部使用 ReentrantLock + Condition 实现等待逻辑。当所有线程调用 barrier.await() 并最终被唤醒时:
- 最后一个到达的线程会执行 unlock → signalAll → 新建 generation → 重置 count
- 其他线程在 await() 返回前,必然经历了 lock → await() 阻塞 → 被 signal → lock 重新获取的过程
- 根据 JMM 的监视器锁规则:前一个线程对 lock 的 unlock 操作 happens-before 后续线程对该 lock 的 lock 操作
- 这意味着:在 await() 返回前写入的变量(如结果数组、状态标志),对后续从 await() 返回的线程是可见的
屏障动作(barrierCommand)天然具有 happens-before 链
若构造 CyclicBarrier 时传入了 Runnable barrierCommand(例如合并中间结果),该任务由最后一个到达的线程在放行前执行:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- barrierCommand.run() 发生在所有线程 await() 返回之前
- 而它的执行又发生在该线程 unlock 锁之前
- 因此:barrierCommand 中对共享变量的写 → unlock → 其他线程 lock → await() 返回 → 读取这些变量,形成完整 happens-before 传递链
- 例如:线程 A 在 barrierCommand 中设置 result = compute(); 线程 B 在 await() 返回后读 result,能安全看到更新值
需自行配合 volatile 或 final 保证跨轮次可见性
CyclicBarrier 支持重复使用(cyclic),但每轮对应一个独立的 Generation 对象。JMM 不自动保证上一轮写入的变量对下一轮线程可见:
- 如果某变量(如统计总数 total)在多轮中持续累加,且由不同轮次的线程修改,仅靠 barrier.await() 不足以让下一轮线程看到上一轮的写入
- 此时应将该变量声明为 volatile(保证每次读都从主存加载),或用 AtomicInteger 等原子类封装
- final 字段在构造完成时的写入,也通过对象初始化规则对后续访问该对象的线程可见,适合只初始化一次的配置数据
对比 CountDownLatch 更强调“一次性”同步语义
CountDownLatch 的 await() 返回后,其内部 state 的修改(decrement)与锁释放构成明确的 happens-before;而 CyclicBarrier 因可重用,其同步边界以“每轮”为单位:
- 同一轮内:所有线程 await() 返回后,彼此能看到该轮 barrierCommand 和各线程在 await() 前写入的变量(前提是这些写操作与锁同步路径连通)
- 跨轮次:无自动 happens-before,需显式借助 volatile、synchronized 或原子变量维持可见性
- 简单说:CyclicBarrier 保障的是“本轮同步点前后”的可见性,不是“所有历史写入”的全局可见性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










