Volatile
Volatile
volatile 作用
- 禁止指令重排
- 可见性
JVM层面实现
jvm实现volatile是使用指令屏障实现的
汇编层面实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opiQdYpm-1602603909947)(图片/Volatile/image-20201009233018270.png)]
汇编使用lock指令实现+MESI缓存一致性协议实现的
Lock指令相当于一条内存屏障,指令重排时,不能把后面的指令重排到内存屏障之前的位置.
以前的CPU lock都是用于锁定总线实现的,但是这样效率太低了
现在的CPU 使用锁缓存来代替锁总线
多处理器、多线程环境下,若某个线程对声明了volatile的变量进行写操作,JVM会向处理器发送一条LOCK前缀的指令,将这个变量所在缓存行的数据写回主内存,LOCK前缀指令通过 “锁缓存” 可以确保回写主内存的操作是原子性的。
但是,其它处理器的缓存中存储的仍然是 “旧值” ,并不能保证可见性,因此,还要借助缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己的缓存值是否过期,当处理器发现自己缓存行对应的内存地址被修改时,就会设置当前缓存行为无效,需要对数据进行修改的时候会重新从主内存中加载。如此,便保证了可见性。
缓存一致性协议
所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(同一个指令周期中,只有一个CPU缓存可以读写内存)。
CPU缓存不仅仅在做内存传输的时候才与总线打交道,而是不停在嗅探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表它所属的处理器去读写内存时,其它处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其它处理器马上知道这块内存在它们的缓存段中已失效。
MESI 是最主流的缓存一致性协议,在MESI协议中,每个缓存都有一个标志位来标记缓存行的状态,可以用2bit来实现.它们分别是
每个核心都维护自己的Cache的状态。如果对于同一份内存数据在多个核里都有cache,则状态都为S(shared)。一旦有一核心改了这个数据(状态变成了M),其他核心就能瞬间通过ringbus(环形总线)感知到这个修改,从而把自己的cache状态变成I(Invalid),并且从标记为M的cache中读过来。同时,这个数据会被原子的写回到主存。最终,cache的状态又会变为S。
https://www..com/article/81043901/
https://www.zhihu.com/question/65372648