jmm是定义线程间共享变量可见性、原子性、有序性的抽象规范,而非物理内存结构;其核心是通过volatile、synchronized、final等机制保障多线程并发安全。

Java 内存模型(JMM)不是内存布局图,而是定义线程间如何看到彼此写入的共享变量的一套规则。配它,不靠堆参数、不调GC,而靠理解“可见性、原子性、有序性”三要素,并用正确手段干预执行语义。
JMM 的核心不是配置 JVM 启动参数,而是编写符合 JMM 规则的代码
真正要“配”的,是代码中对共享状态的操作方式——让 JVM 能按你预期的方式同步、发布、重排序。
一、用 volatile 确保可见性与禁止重排序
volatile 是最轻量级却常被误用的 JMM 工具。它不保证原子性(i++ 仍需 synchronized 或 AtomicInteger),但能解决两个关键问题:
- 写操作立即刷回主内存,读操作强制从主内存加载(打破工作内存缓存)
- 禁止指令重排序:volatile 写之前的所有操作,不能被重排到写之后;volatile 读之后的所有操作,不能被重排到读之前
常见误用:
- 把 volatile 当锁用(比如只靠它保护复合操作)
- 对非基本类型对象引用加 volatile,却忽略其内部字段仍可能不可见
正确姿势:
- 标志位控制流程(如
volatile boolean running = true;) - 作为安全发布的“哨兵”(如构造完对象后,再写 volatile 引用)
- 配合 double-checked locking 中的 instance 字段(JDK5+ 才真正可靠)
二、用 synchronized / Lock 建立 happens-before 关系
synchronized 不只是互斥,更是 JMM 的“同步栅栏”:
- 进入同步块前,清空工作内存中该锁关联变量的副本(下一次读必须从主内存加载)
- 退出同步块时,将修改强制写回主内存
- 锁的获取与释放构成 happens-before 链,天然串起多线程间的操作顺序
注意点:
- 锁对象必须是同一个实例(静态方法锁 Class 对象,实例方法锁 this)
- 不要锁 String 常量或 Integer 等可能被池化复用的对象(易导致意外锁竞争)
- try-finally 保证 unlock(Lock 接口需显式释放)
三、用 final 实现安全初始化与无条件可见性
final 字段在构造器内完成赋值后,只要对象本身被正确发布(如通过 volatile 引用、锁内发布、static 初始化),其他线程就能看到其初始化值——且无需额外同步。这是 JMM 提供的“免费可见性”。
典型场景:
- 不可变类(Immutable Class):所有字段 final + 无 setter
- 构造器内完成依赖注入(如
this.service = new Service();) - 静态单例(枚举或 static final Holder 模式)
⚠️ 注意:final 只保障字段本身不可变,不保障其引用对象内部状态(如 final List list = new ArrayList();,list 内容仍可被并发修改)
四、验证是否真“配对”了 JMM 行为
没有观测的 JMM 理解等于纸上谈兵。推荐三类验证方式:
- 代码层面:用 JMM 规则反推执行结果(如判断某段代码是否存在数据竞争)
- 工具层面:用 JCStress(Oracle 官方并发测试框架)跑原子性/可见性用例
-
运行时层面:开启
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis)看 volatile/synchronized 编译后的内存屏障指令(如lock addl $0x0,(%rsp))
JMM 不是开关,也不是配置项。它是你写每行共享变量操作时,脑子里该有的那张因果图。配得好,程序稳;配得错,bug 藏得深、复现难、定位苦。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











