Java 内存模型 JMM (Java Memory Model)
要搞懂并发编程必须搞懂 JMM.
JMM 屏蔽了不同操作系统中的内存差异性.定义了线程和主内存之前的抽象关系。
线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存存储了该线程以读/写共享变量的副本。本地内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。
JMM 抽象示意图如下:
从上图来看,线程A 和 B 操作的共享变量都是本地内存中的副本,如果想要互相看到对方操作之后的共享变量,就需要给本地内存的共享变量副本刷到主内存里面去,同时另一个线程从主内存读取共享变量。
JMM 通过控制主内存与每个线程的本地内存之间的交互,来为 我们提供内存可见性保证。
happens-before 原则
这个有点绕。。。需要好好理解一下。
JSR-133 使用 happens-before 来阐述操作之间的内存可见性。
在 JMM 中 如果操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。
与我们密切相关的 happens-before 规则如下:
-
程序顺序规则
一个线程里面的每一个操作,happens-before 于该线程中的任意后续操作。
直白点就是 一个线程里面每一个操作的结果 对于后面的操作都是可见的。
-
监视器锁规则
对一个锁的解锁,happens-before 于随后对这个锁的加锁。
翻译一下 大概是 某个持有锁的线程操作的结果在释放之后, 对获取该锁的线程可见
-
volatile 变量规则
对一个 volatile 域的写,happens-before 于任意后续对这个 volatile 的读。
翻译版: 对 volatile 的写,对于后续 读该 volatile 域的线程是可见的。volatile 的 内存语义。
-
传递性
如果 A happens-before B ,B happens-before C 那么 A happens-before C
volatile 变量 写 -> 读 的内存语义
volatile 变量自身具有下列特性:
-
可见性
对一个 volatile 变量的读,总是能看到(任意线程)对这个volatile 变量最后的写入。
-
原子性
对任意单个 volatile 变量的读/写具有原子性,但类似于volatile++这种复合操作不具备原子性。
上面是 volatile 自身的特性,如下是 JDK5之后增强的内存语义。
从JSR-133 开始(JDK5开始),volatile 变量的写 – 读 可以实现线程之间的通信。
从内存语义的角度来看,volatile 的写-读 和 锁的释放-获取有相同的内存效果(参考上面监视器规则)。
volatile 写
当写一个 volatile 的变量时,JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存当中。
volatile 读
当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效,该线程将从主内存中读取共享变量。
参考资料
-
The Java® Language Specification
- Java 并发编程的艺术
-
JSR-133