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