JMM 内存模型与 happens-before 规则
1. 为什么需要 JMM?
Section titled “1. 为什么需要 JMM?”Java 程序运行在多核 CPU 上,每个线程有自己的工作内存(CPU 缓存 / 寄存器),不会直接读写主内存。
这导致三类问题:
| 问题 | 含义 | 典型症状 |
|---|---|---|
| 可见性 | A 线程的写,B 线程不一定能看到 | B 永远读到初始值 |
| 原子性 | 操作不可分割性被破坏 | i++ 丢失更新 |
| 有序性 | 编译器 / CPU 对指令重排序 | 对象”半初始化”就被其他线程读到 |
2. JMM 的 8 个原子操作
Section titled “2. JMM 的 8 个原子操作”JMM 用 8 个操作描述主内存与工作内存之间的交互(了解即可,实际编程不直接用):
read → load → use → assign → store → writelock / unlock(针对主内存变量)read / load:从主内存读取到工作内存use / assign:线程执行引擎与工作内存交互store / write:从工作内存写回主内存
3. happens-before 规则
Section titled “3. happens-before 规则”JMM 不保证”随时可见”,但通过 happens-before 给出确定性承诺:
3.1. 六条核心规则
Section titled “3.1. 六条核心规则”| 规则 | 内容 |
|---|---|
| 程序顺序规则 | 同线程内,前面的操作 hb 后面的操作 |
| 监视器锁规则 | unlock hb 后续对同一锁的 lock |
| volatile 变量规则 | volatile 写 hb 后续对同变量的读 |
| 线程启动规则 | start() hb 新线程中的任意操作 |
| 线程终止规则 | 线程所有操作 hb join() 返回 |
| 传递性规则 | A hb B,B hb C,则 A hb C |
4. 三大问题的解决手段
Section titled “4. 三大问题的解决手段”可见性 → volatile / synchronized / final原子性 → synchronized / Lock / Atomic 原子类有序性 → volatile(内存屏障)/ synchronized5. 面试高频问题
Section titled “5. 面试高频问题”Q:JMM 与 JVM 内存结构(堆/栈/方法区)是一回事吗?
A:不是。JMM 是并发语义规范,描述线程间如何共享变量;JVM 内存结构是运行时内存划分,描述数据存在哪里。两者维度不同。
Q:happens-before 能保证原子性吗?
A:不能。happens-before 只保证可见性和有序性,原子性需要 synchronized 或原子类来保证。
Q:指令重排序有哪几个层次?
A:三个层次——①编译器重排序;②CPU 指令级并行重排序;③内存系统重排序(缓存、写缓冲区)。JMM 通过内存屏障(Memory Barrier)屏蔽底层差异。