Java 内存模型是什么?¶
结论¶
Java 内存模型(JMM,Java Memory Model) 是一种规范,定义了多线程之间如何通过内存共享变量进行交互,以及在并发场景下可见性、有序性、原子性如何保证。它不是 JVM 堆/栈那种"运行时内存分区",而是并发语义层面的抽象。
JMM 解决什么问题¶
现代 CPU 有多级缓存、指令重排,编译器也会优化。这会导致一个线程写的变量,另一个线程看不到;或者代码执行顺序和源码不一致。JMM 规定了:
- 原子性(Atomicity):操作不可分割。
- 可见性(Visibility):一个线程的写对其他线程立即可见。
- 有序性(Ordering):禁止指令重排的某些组合。
主内存与工作内存¶
JMM 抽象:
- 主内存(Main Memory):所有共享变量存储在这里。
- 工作内存(Working Memory):每个线程私有,保存该线程用到的变量副本。
线程不能直接读写主内存,必须先把变量复制到工作内存,操作完再写回主内存。
happens-before 原则¶
JMM 通过 happens-before 规则描述操作之间的可见性与顺序,这是最核心的一组规则:
- 程序顺序规则:同一线程中,前面的操作 happens-before 后面的操作。
- 监视器锁规则:unlock happens-before 后续对同一把锁的 lock。
- volatile 变量规则:volatile 写 happens-before 后续对该变量的读。
- 传递性:A happens-before B,B happens-before C,则 A happens-before C。
- 线程启动规则:
Thread.start()happens-before 该线程的每个动作。 - 线程终止规则:线程中所有操作 happens-before 其他线程检测到该线程结束(
join()返回)。 - 对象终结规则:对象构造完成 happens-before
finalize()开始。
关键字的 JMM 语义¶
- volatile:保证可见性 + 禁止指令重排(不保证原子性,除了单个 long/double 的原子性)。
- synchronized:保证原子性 + 可见性 + 有序性(lock/unlock 语义)。
- final:初始化过程禁止重排,保证构造完成后 final 字段对其他线程可见。
重排序类型¶
- 编译器优化重排
- CPU 指令重排
- 内存系统重排(缓存导致的"乱序"现象)
JMM 通过内存屏障(Memory Barrier)禁止特定类型的重排。
面试加分
- JMM ≠ JVM 内存结构(堆/栈/方法区),前者是并发规范,后者是运行时数据区。
- 双重检查锁定(DCL)单例必须用
volatile,就是为了禁止new Singleton()的指令重排(分配内存、初始化、引用赋值三步可能被重排)。