JMM通过happens-before规则、volatile、synchronized等机制显式控制共享变量的可见性、有序性和原子性,而非自动同步;其核心是定义主内存与线程工作内存间交互的明确约束,确保多线程操作可预测、可控制。

JMM 不是靠“自动同步”来保证并发一致性,而是通过一套明确的规则和程序员可干预的机制,让多线程对共享变量的操作变得可预测、可控制。它的核心不是替你做同步,而是定义“什么时候一个线程的修改对另一个线程可见”“哪些操作必须按顺序执行”“哪些行为算作完整不可拆分”。真正起作用的是这些规则与关键字、内存屏障的配合。
主内存 + 工作内存的隔离模型
所有共享变量都存放在主内存中;每个线程有自己的工作内存(抽象概念,对应 CPU 缓存 + 寄存器),线程不能直接读写主内存,只能操作自己工作内存中的副本。这就天然引入了“不一致”的风险——线程 A 改了副本但没刷回主内存,线程 B 还在读旧副本。
JMM 的作用就是规定:什么时候必须把工作内存的变更写回主内存?什么时候必须从主内存重新加载最新值?这些动作由特定操作触发,比如进入 synchronized 块、写 volatile 变量、解锁、线程启动等。
三大特性的保障机制
并发一致性最终体现为原子性、可见性、有序性三者的协同落实:
立即学习“Java免费学习笔记(深入)”;
- 可见性:靠强制刷新和强制重读实现。volatile 写 → 立即刷到主内存;volatile 读 → 强制从主内存取值;synchronized 退出时 → 把工作内存所有变量刷新回主内存;进入时 → 从主内存读取锁对象关联的所有变量最新值。
- 有序性:靠禁止重排序实现。volatile 修饰的变量,其读写前后不能被编译器或 CPU 重排;synchronized 块内指令也不能跨边界重排;final 字段在构造器内完成赋值后,禁止对其初始化过程重排序,确保其他线程看到的是完全初始化的对象。
- 原子性:JMM 本身只保证基本类型(除 long/double)的读写是原子的;复合操作如 i++ 必须靠外部手段保障。synchronized 提供互斥临界区,Lock 和 AtomicInteger 等则提供更细粒度的原子支持。
happens-before 规则统一判断依据
这是 JMM 最关键的逻辑枢纽。它不关心底层怎么实现,只定义一组“先行发生”关系,只要满足其中任一规则,就能保证前一个操作的结果对后一个操作可见,且执行顺序有保障。常见规则包括:
- 程序顺序规则:同一线程内,前面的语句 happens-before 后面的语句
- 监视器锁规则:unlock 操作 happens-before 后续对同一锁的 lock 操作
- volatile 变量规则:对一个 volatile 变量的写 happens-before 后续对该变量的读
- 线程启动规则:Thread.start() 调用 happens-before 子线程的任意动作
- 传递性:如果 A happens-before B,B happens-before C,则 A happens-before C
这些规则构成一张“可见性图谱”,开发者无需追踪每条指令的内存路径,只需检查代码是否落在某条规则覆盖范围内,就能判断是否存在数据竞争。
JSR133 修复与 final 的初始化安全
早期 JMM 允许“无中生有”的值(out-of-thin-air),比如线程看到一个未完全构造对象的引用。JSR133 引入更强的 final 语义:只要对象构造器中对 final 字段完成赋值,且该对象引用未在构造过程中逸出,那么其他线程通过合法途径获得该引用时,一定能看到 final 字段的正确值,并且不会看到对象处于“半初始化”状态。这为安全发布对象(如单例)提供了语言级保障。


















