写点什么

【并发编程的艺术】JVM 内存模型

发布于: 2021 年 01 月 22 日
【并发编程的艺术】JVM内存模型

系列文章:

【并发编程的艺术】JVM 体系与内存模型

【并发编程的艺术】JAVA 并发机制的底层原理

【并发编程的艺术】JAVA 原子操作实现原理


一 内存模型基础

并发编程模型的关键问题:

1、线程间如何通信,即线程间进行信息交换。通信机制有两种:共享内存和消息传递;

2、线程间如何同步

“同步”指程序中用于控制不同线程间操作发生相对顺序的机器。

在共享内存通信方式下,同步是显式的,因为使用者/开发者必须显式指定某个方法或代码段需要在线程之间进行互斥执行;

在消息传递方式下,同步是隐式的,这是因为消息的发送必须在消息的接收之前。

二 内存模型的抽象结构

2.1 通信机制

Java 内存模型 JMM 决定了一个线程对共享变量的写入何时对另一个线程可见。抽象地说,JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有自己的一个私有本地内存(Local Memory),本地内存中存储了该线程读/写共享变量的副本。需要注意的是,本地内存只是一个虚拟概念,并不真实存在。所谓的本地内存,涵盖缓存、写缓冲区、寄存器和其他的硬件和编译器优化。 JMM 的示意如下图所示:

从图中我们可以看到,两个线程 A 和 B 要进行通信的话,需要通过 2 个步骤:

1、线程 A 把本地内存 A 中更新过的共享变量刷新到主内存中;

2、线程 B 到主内存中读取线程 A 更新过的共享变量。

以两个线程对一个共享变量 x 进行操作时,AB 之间通信过程为例,示意如下:

假设开始时,A、B 的本地内存和主存中的 x 值都是 0.A 执行时,把 x 更新后的值(假设=1)临时存放在本地内存 A 中,A 和 B 需要通信时,线程 A 会先把自己本地内存中修改后的 x 值刷新到主内存中,此时主内存中的 x=1;再之后,线程 B 到主内存中读取由线程 A 更新后的 x 值,在这之后,B 本地内存中的 x=1.

综上所述,这两个步骤,实际上就是 A 在向 B 发送消息,并且,这个通信过程必须经过主内存。JMM 通过控制主内存与每个线程本地内存之间的交互,来为我们开发者提供内存可见性保证。

2.2 指令重排序

指令重排序,是编译器和处理器为提高性能而对指令的执行顺序重新排列。有三种:

1)编译器优化的重排序

不改变单线程程序语义

2)指令级并行的重排序(Instruction-Level Parallelism, ILP)

现在处理器采用了指令级并行技术将多条指令重叠执行。如果不存在数据依赖,那么处理器可以改变语句对应的机器指令的执行顺序。

3)内存系统的重排序

处理器使用缓存和读/写缓冲区,这会导致加载和存储操作看起来是乱序执行。


上图是 Java 源代码到最终执行的指令序列的过程。

2.3 内存屏障

上节描述的三种重排序,可能会导致多线程程序出现内存可见性问题。因此,JMM 的编译器重排序规则,会禁止某些特定类型的编译器重排序(不是全部)。对处理器重排序,JMM 会要求 Java 编译器在生成指令序列时,插入特定类型的内存屏障指令,通过这个屏障来禁止指定类型的处理器重排序。


尽管 CPU 写缓冲区有很多好处,但仅对它所在的处理器可见,这个特性会对内存操作的执行顺序产生极大的影响:处理器对内存的读/写操作的执行顺序,可能与内存实际发生的读/写顺序不一致!!!

下图是一个示例:


上面示例产生问题的原因如下图所示:

当 A 和 B 可以同时把共享变量写入自己的写缓冲区(A1,B1),然后再从内存中读取另一个共享变量(A2,B2),最后才把自己写缓冲区中保存的脏数据刷新到内存(A3,B3)。当执行顺序是按照这种方式进行时,就会得到上面的结果。

常见的处理器重排序规则:

常见的处理器都允许 Store-Load 重排序,一般都不允许对存在数据依赖的操作做重排序。

JMM 中的内存屏障指令分为 4 类:

happens-before 规则:

1)程序顺序规则

2)监视器锁规则

3)volatile 变量规则:对一个 volatile 字段的写,happens-before 于任意后续对这个 volatile 字段的读。

4)传递性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。

happens-before 与 JMM 的关系如下图所示:


发布于: 2021 年 01 月 22 日阅读数: 27
用户头像

磨炼中成长,痛苦中前行 2017.10.22 加入

微信公众号【程序员架构进阶】。多年项目实践,架构设计经验。曲折中向前,分享经验和教训

评论

发布
暂无评论
【并发编程的艺术】JVM内存模型