JMM

#Concurrency #JAVA #JVM

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 原则主要用于保证并发环境下的可见性有序性

常见规则

  1. 程序顺序规则:在一个线程内,书写在前面的代码操作 happens-before 书写在后面的代码操作。
  2. 监视器锁规则:对一个锁的解锁操作 happens-before 后续对同一个锁的加锁操作。
  3. volatile 变量规则:对一个 volatile 变量的写操作 happens-before 后续对该变量的读操作。
  4. 传递性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。
  5. 线程启动规则:线程的 start() 方法 happens-before 于此线程的每一个动作。
  6. 线程终止规则:线程中的所有操作都 happens-before 对此线程的终止检测。
  7. 线程中断规则:对线程 interrupt() 方法的调用 happens-before 发生于被中断线程的代码检测到中断时。
  8. 对象终结规则:一个对象的初始化完成(构造函数执行结束)happens-before 它的 finalize() 方法的开始。

总结

JMM 通过抽象出主内存和本地内存,并提供一套 happens-before 规则,成功地为 Java 开发者屏蔽了底层硬件的复杂性。理解 JMM 有助于我们编写出正确、可靠的并发程序,避免可见性和有序性带来的问题。