Java Memory Model Java内存模型(JMM),定义了线程如何与内存交互及线程间的可见性、有序性和原子性。
JMM屏蔽了各种硬件和操作系统的访问差异,保证Java程序在各种平台下对内存的访问都能保证一致效果。
1 JMM
可见性 | 一个线程对共享变量的修改,其他线程能立即感知。 |
原子性 | 操作不可中断,要么全部执行成功,要不完全不执行。 |
有序性 | 代码执行顺序与代码编写顺序一致,避免因指令重排带来的问题。 |
表 JMM的核心目标
1.1 内存结构
主内存 | 所有线程共享的内存区域,存储共享变量的原始值。 |
工作内存 | 每个线程私有的内存区域,保存该线程使用的共享变量副本。 |
表 JMM的内存结构
类型 | 作用域 | 介绍 |
lock | 主内存 | 锁定,把一个变量标识为线程独占状态。 |
unlock | 主内存 | 解锁,把一个处于锁定状态的变量释放。 |
read | 主内存 | 读取,把一个变量的值从主内存传输到工作内存中。 |
load | 工作内存 | 加载,把read操作的变量值保存到工作内存的变量副本中。 |
use | 工作内存 | 使用,把工作内存中的变量值传输给执行引擎。 |
assign | 工作内存 | 赋值,执行引擎的值存放到工作内存的变量副本中。 |
store | 工作内存 | 存储,把一个工作内存变量副本的值传送到主内存中。 |
write | 主内存 | 写入,把store操作的变量值保存到主内存的变量中。 |
表 JMM的8种内存交互行为
2 指令重排
编译器和处理器为了优化程序性能而对指令执行顺序进行重新排列的一种机制。
2.1 三个层级的指令重排
在保证单线程执行结果不变的前提下,指令重排序会发生在三个层级:
1、编译器重排序
Java编译器(javac、JIT)在生成字节码时,会调整无关指令顺序来提高寄存器利用率或减少指令缓存未命中。
2、处理器指令级并行重排序
CPU通过乱序执行和指令级并行动态调整指令执行顺序,来充分利用流水线资源。
3、内存系统重排序
由于CPU缓存、写缓冲区和缓存一致性协议(如MESI)的存在,可能会对指令重排。
2.2 指令重排序的约束
指令重排序在单线程环境下不会影响程序逻辑,但在多线程环境下可能会导致可见性、原子性和有序性问题。
2.2.1 数据依赖性约束
写后读 | Read After Write,RAW。 例子: int a = 1; int b = a; // b依赖a的值 RAW |
写后写 | Write After Write,WAW。主要是防止多线程环境下出问题。 例子:int a =1; a =2; // 第二次写不能排到第一次写之前 WAW |
读后写 | Write After Read,WAR。 例子:int b = a; a = 3; |
表 数据依赖类型
2.2.2 happens-before 规则
单程序顺序规则 | 单线程内代码的书写顺序决定操作顺序。 |
锁规则 | 对一个锁的解锁happens-before 后续对这个锁的加锁动作。 作用:确保锁释放前的修改对下一个锁持有者可见。 |
volatile规则 | 对一个volatile变量的写操作happens-before后续对这个变量的读操作。 作用:确保对volatile变量的修改对所有线程立即可见。 |
线程启动规则 | 线程的start()方法调用happens-before 该线程内的所有操作。 作用:确保父线程在启动子线程前的修改对子线程可见。 |
线程终止规则 | 线程中所有操作happens-before其他线程检测到该线程已终止。 作用:确保子线程的修改在join()后对父线程可见。 |
中断规则 | 对线程interrupt()方法调用happens-before被中断线程检测到中断事件。 |
对象终结规则 | 对象的构造函数执行结束happens-before它的finalize()方法被调用。 |
传递性 | 如果操作A happens-before操作B,且操作B happens-before操作C,则操作A happens-before 操作C。 |
表 happens-before 规则
happens-before 保障了可见性:如操作A happens-before 操作B,则A对共享变量的修改对B可见;
2.2.3 内存屏障
是用来禁止特定类型指令重排序的处理器指令,确保内存操作的可见性。
LoadLoad | 禁止该屏障前的读操作与屏障后的读操作重排序。 |
StoreStore | 禁止该屏障前的写操作与屏障后的写操作重排序。 |
LoadStore | 禁止该屏障前的读操作与屏障后的写操作重排序。 |
StoreLoad | 禁止该屏障前的写操作与屏障后的读操作重排序。(开销最大) |
表 内存屏障类型