Java Memory Model(JMM,Java 内存模型)是 Java 虚拟机(JVM)中用于定义线程之间共享变量如何在内存中读写的规范和机制。它解决了在并发编程中最核心的两个问题:
可见性(Visibility)
有序性(Ordering)
下面是对 Java Memory Model 的系统性介绍:
🧠 一、Java Memory Model 的目的
JMM 的设计目标是为了屏蔽底层硬件和操作系统的差异,让 Java 程序在不同平台上的多线程行为具备一致性。它定义了:
线程之间共享变量的访问规则。
JVM 以及编译器在执行时对代码重排序的边界。
volatile、synchronized 等关键字的语义保证。
📦 二、主内存与工作内存
在 JMM 中,每个线程有:
主内存(Main Memory):所有共享变量的真实存储位置。
工作内存(Working Memory):每个线程自己的缓存副本,用于提高性能。
执行流程:
线程从主内存中读取共享变量副本到工作内存。
对变量的所有读写必须在工作内存中进行。
最终会将修改后的值刷新回主内存。
这就会引发“可见性”问题 —— 一个线程对变量的修改,另一个线程可能看不到。
🔁 三、JMM 中的重排序
现代处理器和编译器为了优化性能,会对指令进行 重排序(Reordering)。JMM 允许以下三种重排序:
编译器优化重排序:编译器在不改变单线程语义的前提下,调整代码顺序。
处理器指令重排序:CPU 在执行时对指令重新排序。
内存系统重排序:不同线程看到的变量修改顺序不同。
JMM 为此引入了 happens-before 原则,来规范什么顺序是“合法”的。
⛓️ 四、Happens-Before 原则
happens-before 是 JMM 的核心原则,它规定了操作之间的执行先后关系,用于保证可见性与有序性。
一些常见的 happens-before 规则:
程序顺序规则:一个线程内,前面的操作先于后面的操作。
监视器锁规则:对一个锁的 unlock 操作先于对同一个锁的 lock。
volatile 规则:对一个 volatile 变量的写操作先于后续对该变量的读操作。
线程启动规则:Thread.start() 先于该线程的任何操作。
线程终止规则:线程中的所有操作先于其他线程检测到它结束。
🧩 五、volatile 与 JMM
volatile 关键字是 JMM 的直接产物,它有两个核心语义:
保证可见性:写入一个 volatile 变量,相当于刷新到主内存;读取 volatile 变量前强制从主内存拉取。
禁止指令重排序:它会插入内存屏障,防止该变量周围的操作被重排序。
但它不保证原子性,比如 i++ 不是原子操作,即使 i 是 volatile。
🔒 六、JMM 与 synchronized
synchronized 同样依赖 JMM:
进入 synchronized 块时:读取变量并清空工作内存。
退出 synchronized 块时:刷新变量到主内存。
这保证了代码块中的变量读写对其他线程是可见且有序的。
⚠️ 七、JMM 的实际意义
理解 JMM 可以帮助:
正确使用 volatile、synchronized 和 Atomic 工具类。
避免并发编程中的“脏读”、“乱序执行”、“不可见”等问题。
编写线程安全的类与组件。