Java内存模型JMM
1.概念
资料引用:
Java内存模型(Java Memory Model,JMM)JMM规定了线程和内存之间的一些关系。
系统存在一个主内存(Main Memory)
,Java中所有变量都储存在主存中,对于所有线程都是共享
的。
每条线程都有自己的工作内存(Working Memory)
,工作内存中保存的是主存中某些变量的拷贝
,线程对所有变量的操作都是在工作内存中进行,线程私有
,变量传递均需要通过主存完成。
2.主内存与工作内存
处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存
,高速缓存会有缓存一致性
问题,
如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议
来解决这个问题。
所有的变量都存储在主内存中,每个线程还有自己的工作内存 ,工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的主内存副本拷贝。
线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成
3.数据类型存储
- 方法中的
基本类型
本地变量将直接存储在工作内存
的栈帧
结构中; 引用类型
的本地变量:引用存储在工作内存
,实际存储在主内存
;成员变量
、静态变量
、类信息
均会被存储在主内存
中;- 主内存共享方式是
线程各拷贝一份数据到工作内存
中,操作完成后就刷新到主内存
4.内存之间的交互
遵循交互协议
- 一个变量如何从主内存拷贝到工作内存
- 如何从工作内存同步回主内存子类
JMM规定了8种操作(原子性)
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
unclock(解锁):作用于主内存的变量,把一个处于锁定的状态释放出来。
read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中
load(载入):作用于工作内存的变量,把read操作从主内存 得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的 值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,把一个从执行引擎接收到的值 赋值给工作内存的变量,每当虚拟机遇到一个给变 量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传递到主内存,以便write操作使用。
write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
5.JMM 3大特性
- 原子性 - 保证指令不会受到线程上下文切换的影响
- 可见性 - 保证指令不会受 cpu 缓存的影响
- 有序性 - 保证指令不会受 cpu 指令并行优化的影响
6.指令重排序
哪些情况不能指令重排序?
1.在单线程环境下不能改变程序的运行结果
2.存在数据依赖关系的不允许重排序
happens-before规则
-
程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
-
锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
-
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
-
传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
-
线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
-
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
-
线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
-
对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始