jmm
2021-05-21 14:59:10 6 举报
jmm简单描述
作者其他创作
大纲/内容
CPU1
又会带来其他问题:缓冲数据什么时间提交到主存,什么时间通知其他cpu是不确定的,没有顺序的。存在执行乱序问题
i=2M
内存
i=1S
ok
写
store
线程1
volatile a;a=1;storestorea=1storeload
不存在数据依赖重叠执行指令
线程1写:先写入本地内存,在同步到主内存。线程2读:先读本地内存,不存在或失效在读主内存这种内存结构是基于操作系统的逻辑虚拟出来的结构,并不是真实存在的,可以屏蔽各种硬件和操作系统的差异性,实现平台一致性。而且和jvm的运行时结构也没关联。主内存存储的是实例字段,静态字段和数组对象线程共享的数据。本地内存可以理解为主内存数据的拷贝。所有的线程只能操作本地内存,线程间的通信需要通过主内存。
右线程读取i值
CPU:增加了高速缓存 L1 L2 L3操作系统:增加了进程和线程,利用cpu的时间切片编译器:指令优化,重排
处理速度由高到低
不同的平台的内存屏障不同,JMM就是屏蔽了平台相关问题。
读:read,load 顺序执行。写:store,write 顺序执行。Java内存模型只要求上述两个操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的。
I/O设备
M,E,S都可以直接读。I要从主内中读取。M,E可以直接写。S的写需要其他cpu的缓存设置I才可以写。
主内存
这种优化,就会带来线程安全的问题,缓存一致性行问题
最终指令序列
处理器缓存导致乱序
CPU1Regs-->L1--> L2
i=2S
内存i=2
LoadLoad Barriers:确保Load1数据的装载先于Load2以及所有后续装载指令StoreStore Barriers:确保Store1的数据对其他处理器可见(会使缓存行无效,并刷新到内存中)先于Store2及所有后续存储指令的装载LoadStore Barriers:确保Load1数据装载先于Store2及所有后续存储指令刷新到内存StoreLoad Barriers:确保Store1数据对其他处理器可见(刷新到内存,并且其他处理器的缓存行无效)先于Load2及所有后续装载指令的装载。该指令会使得该屏障之前的所有内存访问指令完成之后,才能执行该屏障之后的内存访问指令
参见:https://docs.oracle.com/javase/specs/jls/se14/html/jls-17.html#jls-17.4.5
程序次序规则(Program Order Rule):在同一个线程中,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操纵。准确的说是程序的控制流顺序,考虑分支和循环等。管理锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面(时间上的顺序)对同一个锁的lock操作。(同步语义)volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面(时间上的顺序)对该变量的读操作。(同步语义)线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。(同步语义)线程终止规则(Thread Termination Rule):线程的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断时事件的发生。Thread.interrupted()可以检测是否有中断发生。对象终结规则(Finilizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()的开始。(同步语义)传递性(Transitivity):如果操作A 先行发生于操作B,操作B 先行发生于操作C,那么可以得出A 先行发生于操作C。
CPU 层面提供了 memory barrier(内存屏障)的指令,从硬件层面来看这个 memroy barrier 就是 CPU flush storebufferes 中的指令。软件层面可以决定在适当的地方来插入内存屏障。
CPU2
S-I
read
内存重排序C
可见:一个线程操作共享数据的结果要被其他线程看到(读到)。
高速缓存 变量副本
CPU2Regs-->L1--> L2
通过内存屏障可解决
线程1的本地内存
i=1I
左线程修改i值
重排序导致原子性,可见性,有序性的问题
L3
编译器优化重排序A
线程2的本地内存
主内存变量
指令并行重排序B
load
线程2
use
重排序的优化
原子:一个操作不能被打断,要么全部执行完毕,要么不执行。
save
unlock
cpu每次的读写都是先经过缓存在到内存的。这样cpu在高速运作时就不需要等待内存的处理了。这样就会带来缓存一致性的问题,不同cpu同时缓存了内存的数据,操作数据时就会出现各个缓存数据不一致的问题。
线程3
乱序的理解
lock
引入storebufferes解决阻塞
i=2E
storebuffere
有序:单线程内表现为串行语义。多线程“指令重排”现象和“工作内存和主内存同步延迟”现象
JMM
等待
assign
不改变单线程语义重新安排执行顺序
源码
解决方法:总线索,在内存和缓存间加锁---变慢。缓存锁,细粒度的锁,通过缓存一致性协议MESI实现
CPU
write
数据依赖性: 处理器不会对存在数据依赖的操作进行重排序。as-if-serial语义: 不管怎么重排序(单线程)程序的执行结果不能被改变。先行发生原则(happens-before):如果操作A线性发生于操作B,则A产生的影响能被操作B观察到,不可重排。
内存i=1
可见性的理解
0 条评论
回复 删除
下一页