Java内存模型

定义

并发编程中,需要处理两个关键问题:

  1. 线程之间如何通信
  2. 线程之间如何同步

通信指线程之间以何种机制来交换信息,线程之间的通信机制有两种:共享内存消息传递

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

Java并发采用的是共享内存模型,Java线程之间的通信由Java内存模型(JMM)控制。Java内存模型的主要目的是定义程序中各种变量的访问规则。

主内存和本地内存

JMM规定了线程之间共享变量存储在主内存中,每个线程都有私有的本地内存,本地内存存储了共享变量的副本,Java内存模型的示意图如图所示:

Java内存模型抽象结构

从图来看,线程A和线程B之间要通信的话,会进行以下操作:

  1. 线程A把本地内存中更新过的共享变量刷新到主内存中。
  2. 线程B去主内存中读取线程A之前更新的变量。

内存间的交互操作

关于如何将一个变量从主内存拷贝到本地内存中,JMM定义了以下八种操作来完成,JVM必须保证每种操作是原子性的。

  • lock:作用于主内存的变量,将一个变量标识为一个线程独占状态。
  • unlock:作用于主内存的变量,将处于线程独占状态的变量释放出来。
  • read:作用于主内存的变量,将一个变量的值从主内存传输到线程的本地内存中。
  • load:作用于本地内存的变量,将read操作得到的变量放入本地内存的变量副本中。
  • use:作用于本地内存的变量,将本地内存的一个变量值传递给执行引擎。
  • assign:作用于本地内存的变量,它把一个从执行引擎接收到的值赋值给本地内存中的变量。
  • store:作用于本地内存的变量,将本地内存的值传送到主内存中
  • write:作用于主内存的变量,将store操作得到的变量值放入主内存的变量中。

重排序

重排序时指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,重排序分为三种类型:

  1. 编译器优化的重排序
  2. 指令并行的重排序,处理器使用指令级并行技术来将多条指令重叠执行。
  3. 内存系统的重排序

重排序会导致多线程程序出现内存可见性问题,对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序。对于处理器,JMM要求编译器生成指令序列的时候,插入内存屏障指令来禁止重排序。

数据依赖性

如果两个操作访问同一个变量,且两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖性分三种类型,如下表所示:

名称 代码实例
写后读 a = 1; b = a;
写后写 a = 1; a = 2;
读后写 a = b; b = 1;

上述现象如果执行顺序发生改变,执行结果就会被改变。

编译器和处理器在重排序时,会遵守数据依赖性原则,不会改变存在依赖关系的两个操作的执行顺序。

as-if-serial语义

无论怎么重排序,程序的执行结果不能被改变。编译器,runtime和处理器都必须遵循as-if-serial语义。

happens-before

JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。

happens-before原则如下:

  1. 程序顺序规则:一个线程中每个操作,happens-before于该线程中任意后续操作。
  2. 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  3. volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

happens-before具有传递性。

Volatile的内存语义

volatile写:当写一个volatile变量时,JMM会把该线程的本地内存中的共享变量刷新到主内存。

volatile读:当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,线程从主内存读取共享变量。

为了实现volatile的内存语义,编译器在生成字节码的时候,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

锁的内存语义

当线程释放锁的时候,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。

当线程获取锁的时候,JMM会把该线程对应的本地内存设置为无效。从而使得被监视器保护的临界区代码必须从主内存读取共享变量。

参考

《Java并发编程的艺术》

《深入理解Java虚拟机:第三版》

文章作者: L1nker4
文章链接: https://l1n.wang/2020/04/jmm/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 L1nker4's Blog