跳转至

Java 内存模型是什么?

结论

Java 内存模型(JMM,Java Memory Model) 是一种规范,定义了多线程之间如何通过内存共享变量进行交互,以及在并发场景下可见性、有序性、原子性如何保证。它不是 JVM 堆/栈那种"运行时内存分区",而是并发语义层面的抽象

JMM 解决什么问题

现代 CPU 有多级缓存、指令重排,编译器也会优化。这会导致一个线程写的变量,另一个线程看不到;或者代码执行顺序和源码不一致。JMM 规定了:

  • 原子性(Atomicity):操作不可分割。
  • 可见性(Visibility):一个线程的写对其他线程立即可见。
  • 有序性(Ordering):禁止指令重排的某些组合。

主内存与工作内存

JMM 抽象:

  • 主内存(Main Memory):所有共享变量存储在这里。
  • 工作内存(Working Memory):每个线程私有,保存该线程用到的变量副本。

线程不能直接读写主内存,必须先把变量复制到工作内存,操作完再写回主内存。

线程A 工作内存  --read/write-->  主内存  <--read/write--  线程B 工作内存

happens-before 原则

JMM 通过 happens-before 规则描述操作之间的可见性与顺序,这是最核心的一组规则:

  1. 程序顺序规则:同一线程中,前面的操作 happens-before 后面的操作。
  2. 监视器锁规则:unlock happens-before 后续对同一把锁的 lock。
  3. volatile 变量规则:volatile 写 happens-before 后续对该变量的读。
  4. 传递性:A happens-before B,B happens-before C,则 A happens-before C。
  5. 线程启动规则Thread.start() happens-before 该线程的每个动作。
  6. 线程终止规则:线程中所有操作 happens-before 其他线程检测到该线程结束(join() 返回)。
  7. 对象终结规则:对象构造完成 happens-before finalize() 开始。

关键字的 JMM 语义

  • volatile:保证可见性 + 禁止指令重排(不保证原子性,除了单个 long/double 的原子性)。
  • synchronized:保证原子性 + 可见性 + 有序性(lock/unlock 语义)。
  • final:初始化过程禁止重排,保证构造完成后 final 字段对其他线程可见。

重排序类型

  1. 编译器优化重排
  2. CPU 指令重排
  3. 内存系统重排(缓存导致的"乱序"现象)

JMM 通过内存屏障(Memory Barrier)禁止特定类型的重排。

面试加分

  • JMM ≠ JVM 内存结构(堆/栈/方法区),前者是并发规范,后者是运行时数据区。
  • 双重检查锁定(DCL)单例必须用 volatile,就是为了禁止 new Singleton() 的指令重排(分配内存、初始化、引用赋值三步可能被重排)。