JMM
JMM
1. 什么是 JMM?
Java 内存模型(JMM)是一个抽象模型,它定义了线程和主内存之间的抽象关系。JMM 的核心目的是屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果,为开发者提供清晰的并发编程保证。

2. JMM 的核心抽象
JMM 通过以下概念来抽象内存交互:
- 主内存:所有线程共享的区域,存储了所有的实例字段、静态字段和构成数组对象的元素。
- 本地内存:每个线程私有的区域,是 JMM 的一个抽象概念。它存储了该线程对主内存变量的副本。
- 通信原则:线程间的通信必须通过主内存进行。一个线程将本地内存的变量副本写入主内存,另一个线程再从主内存读取到最新的值。

3. JMM 诞生的原因
JMM 的出现主要是为了解决现代计算机体系结构带来的两个核心问题:
- CPU 缓存导致的可见性问题:为了提高速度,每个 CPU 都有自己的高速缓存。当多个线程运行在不同 CPU 上时,它们可能各自持有一个共享变量的缓存副本,导致一个线程的修改对另一个线程不可见。尽管硬件层面有缓存一致性协议(如 MESI)来协调,但 JMM 提供了更高层、更强的可见性保证。
- 指令重排导致的有序性问题:为了优化性能,编译器和处理器可能会对输入的代码进行指令重排序。在单线程环境下,重排序不会影响最终结果,但在多线程环境下,可能会破坏代码原有的执行逻辑,导致意想不到的结果。

4. JMM 的核心工具:Happens-Before 原则
happens-before 是 JMM 中最核心的概念,它定义了两个操作之间的偏序关系。如果操作 A happens-before 操作 B,那么 A 操作的执行结果对 B 操作是可见的,且 A 操作的执行顺序在 B 操作之前。
目的:happens-before 原则主要用于保证并发环境下的可见性和有序性。
常见规则:
- 程序顺序规则:在一个线程内,书写在前面的代码操作
happens-before书写在后面的代码操作。 - 监视器锁规则:对一个锁的解锁操作
happens-before后续对同一个锁的加锁操作。 - volatile 变量规则:对一个
volatile变量的写操作happens-before后续对该变量的读操作。 - 传递性:如果 A
happens-beforeB,且 Bhappens-beforeC,那么 Ahappens-beforeC。 - 线程启动规则:线程的
start()方法happens-before于此线程的每一个动作。 - 线程终止规则:线程中的所有操作都
happens-before对此线程的终止检测。 - 线程中断规则:对线程
interrupt()方法的调用happens-before发生于被中断线程的代码检测到中断时。 - 对象终结规则:一个对象的初始化完成(构造函数执行结束)
happens-before它的finalize()方法的开始。
总结
JMM 通过抽象出主内存和本地内存,并提供一套 happens-before 规则,成功地为 Java 开发者屏蔽了底层硬件的复杂性。理解 JMM 有助于我们编写出正确、可靠的并发程序,避免可见性和有序性带来的问题。