深入理解 Java 内存模型
Java 内存模型(Java Memory Model,简称 JMM)是并发编程中至关重要的一部分,它定义了在多线程环境下变量的可见性、有序性与原子性等关键行为。下面我们从基础到进阶,带你理解 JMM 的核心机制。
一、什么是 Java 内存模型?
JMM 是 Java 虚拟机(JVM)中关于多线程共享内存访问的规范,它描述了:
- 各线程如何将变量从主内存拷贝到工作内存
- 变量在何时对其他线程可见
- 编译器与 CPU 可进行哪些指令重排序优化
JMM 并不是真实存在的一块内存,而是一种抽象规范。
二、JMM 的关键概念
1. 主内存与工作内存
- 主内存(Main Memory):所有线程共享的区域,保存所有变量的真实值。
- 工作内存(Working Memory):每个线程私有,保存变量的副本。
线程操作变量流程如下:
1 | 主内存 ←→ 工作内存 ←→ 执行引擎 |
2. 可见性
当一个线程修改了变量,必须刷新回主内存,否则其他线程看不到这个修改。
使用 volatile 可以保证可见性。
3. 有序性
编译器和处理器会对代码进行指令重排序,但 JMM 保证:
- 单线程内语义不变
volatile、synchronized和final可以部分禁止重排序
4. 原子性
基本数据类型的读写是原子性的,除非是 long 或 double(Java 5 之前)。
原子类如 AtomicInteger 则通过 CAS 实现更高层次的原子性。
三、JMM 中的 happens-before 规则
happens-before 是 JMM 保证线程安全的核心规则。如果一个操作 A happens-before B,那么 A 的结果对 B 可见,且 A 的执行在 B 之前。
常见的规则包括:
- 程序顺序规则:单线程中操作按代码顺序执行。
- 监视器锁规则:解锁
unlock先于加锁lock。 - volatile 变量规则:写
volatile先于读volatile。 - 线程启动规则:
Thread.start()先于新线程所有操作。 - 线程终止规则:线程所有操作先于
Thread.join()返回。 - 传递性:A happens-before B,B happens-before C ⇒ A happens-before C。
四、关键字与内存模型的关系
1. volatile
- 禁止指令重排序
- 保证变量对所有线程可见
- 不保证原子性
2. synchronized
- 保证可见性和原子性
- 同步代码块有入口和退出的内存屏障,线程进入临界区时必须读取主内存,退出时必须写回主内存
3. final
- 保证被赋值后对象引用不可更改
- 在构造函数中正确设置 final 字段能确保构造完成后对其他线程可见
五、典型示例分析
错误示例(未加 volatile):
1 | class Example { |
可能出现的问题:主线程修改了 flag,子线程由于缓存没有同步,导致死循环。
正确写法:
1 | volatile boolean flag = false; |
六、总结
| 特性 | 普通变量 | volatile |
synchronized |
|---|---|---|---|
| 原子性 | ❌ | ❌ | ✅ |
| 可见性 | ❌ | ✅ | ✅ |
| 有序性控制 | ❌ | ✅(部分) | ✅ |
Java 内存模型看似抽象,但在并发编程中起着决定性的作用。如果你在多线程中遇到“看不懂的行为”,十有八九和 JMM 脱不开关系。
以上内容如果有理解不清的地方,欢迎大家留言交流、批评指正~ 咱们继续修炼 Java 的内功心法 🔥!
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.










