JMM内存模型解析
JMM(Java Memory Model,Java 内存模型)是 Java 虚拟机(JVM)规范中定义的一种抽象模型,用于描述多线程环境下,线程如何与内存交互以及如何保证内存的可见性、有序性和原子性。JMM 的主要目标是解决多线程并发编程中的内存一致性问题。以下是 JMM 的主要组成部分及其详细解析:
1. 主内存(Main Memory)
- 定义:
- 主内存是所有线程共享的内存区域,存储了所有的变量(包括实例字段、静态字段等)。
- 特点:
- 主内存是线程之间通信的桥梁。
- 线程对变量的读写操作最终都会反映到主内存中。
2. 工作内存(Working Memory)
- 定义:
- 每个线程都有自己的工作内存,工作内存是线程私有的内存区域。
- 工作内存存储了线程对主内存中变量的副本。
- 特点:
- 线程对变量的所有操作(读取、赋值等)都在工作内存中进行。
- 线程不能直接操作主内存中的变量,而是通过工作内存间接操作。
3. 内存间的交互操作
JMM 定义了以下 8 种原子操作,用于描述主内存和工作内存之间的交互:
| 操作 | 作用 |
|---|---|
lock(锁定) |
作用于主内存的变量,将其标识为线程独占状态。 |
unlock(解锁) |
作用于主内存的变量,释放锁定状态。 |
read(读取) |
作用于主内存的变量,将变量的值从主内存传输到工作内存。 |
load(载入) |
作用于工作内存的变量,将 read 操作得到的值放入工作内存的变量副本中。 |
use(使用) |
作用于工作内存的变量,将变量的值传递给执行引擎。 |
assign(赋值) |
作用于工作内存的变量,将执行引擎接收到的值赋给工作内存的变量。 |
store(存储) |
作用于工作内存的变量,将变量的值传输到主内存。 |
write(写入) |
作用于主内存的变量,将 store 操作得到的值放入主内存的变量中。 |
4. 内存模型的三大特性
JMM 的核心目标是保证多线程环境下的内存可见性、有序性和原子性。
(1) 可见性(Visibility)
- 定义:
- 当一个线程修改了共享变量的值,其他线程能够立即看到修改后的值。
- 实现机制:
- 通过
volatile关键字、synchronized锁、final关键字等保证可见性。
- 通过
(2) 有序性(Ordering)
- 定义:
- 程序执行的顺序按照代码的先后顺序执行。
- 实现机制:
- 通过
volatile关键字、synchronized锁、happens-before规则等保证有序性。
- 通过
(3) 原子性(Atomicity)
- 定义:
- 一个操作是不可中断的,要么全部执行成功,要么全部不执行。
- 实现机制:
- 通过
synchronized锁、java.util.concurrent.atomic包中的原子类等保证原子性。
- 通过
5. Happens-Before 规则
Happens-Before 是 JMM 中定义的一组规则,用于描述操作之间的可见性和有序性。以下是 Happens-Before 的主要规则:
| 规则 | 描述 |
|---|---|
| 程序顺序规则 | 在一个线程中,前面的操作 Happens-Before 后面的操作。 |
| 锁规则 | 解锁操作 Happens-Before 后续的加锁操作。 |
| volatile 变量规则 | 对 volatile 变量的写操作 Happens-Before 后续的读操作。 |
| 线程启动规则 | 线程的 start() 方法 Happens-Before 该线程的任何操作。 |
| 线程终止规则 | 线程的所有操作 Happens-Before 其他线程检测到该线程已经终止。 |
| 线程中断规则 | 对线程的 interrupt() 方法 Happens-Before 被中断线程检测到中断事件。 |
| 对象终结规则 | 对象的构造函数 Happens-Before 该对象的 finalize() 方法。 |
| 传递性规则 | 如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。 |
6. volatile 关键字
volatile 是 JMM 中用于保证可见性和有序性的关键字。
(1) 可见性
- 对
volatile变量的写操作会立即刷新到主内存。 - 对
volatile变量的读操作会从主内存中读取最新的值。
(2) 有序性
- 禁止指令重排序优化,确保
volatile变量的读写操作按照代码顺序执行。
7. synchronized 关键字
synchronized 是 JMM 中用于保证可见性、有序性和原子性的关键字。
(1) 可见性
- 线程在进入
synchronized块时,会清空工作内存中的变量副本,从主内存中重新读取。 - 线程在退出
synchronized块时,会将工作内存中的变量副本写回主内存。
(2) 有序性
synchronized块内的操作不会被重排序到块外。
(3) 原子性
synchronized块内的操作是不可分割的,确保线程安全。
8. final 关键字
final 是 JMM 中用于保证可见性的关键字。
(1) 可见性
- 被
final修饰的字段在构造函数中初始化后,对其他线程立即可见。
(2) 禁止重排序
- 禁止对
final字段的写操作重排序到构造函数之外。
9. 总结
JMM 是 Java 多线程编程的核心基础,它通过定义主内存、工作内存和内存间交互操作,解决了多线程环境下的内存一致性问题。JMM 的三大特性(可见性、有序性、原子性)以及 Happens-Before 规则,为开发者提供了编写线程安全程序的指导。理解 JMM 的组成部分及其工作原理,有助于更好地掌握 Java 并发编程。