什么是内存语义
内存语义:可以简单理解为 volatile,synchronize,atomic,lock 之类的在 JVM 中的内存方面实现原则。
volatile的内存屏障策略非常严格保守,非常悲观且毫无安全感的心态,由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性。
什么是内存屏障
https://www.yuque.com/docs/share/040a4a12-17ad-4dc6-8120-2908fce1d1eb?# 《08.内存屏障》
volatile写的内存语义如下:
在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
线程对变量进行修改之后,要立刻回写到主内存。
在写操作前后各插入一个内存屏障
我插入storestore屏障之后,不管前面是什么操作,爱怎么排序怎么排序,但是普通写和普通读不管怎么操作,不能排序到volatile写的后面来.这就是volatile写的含义.
storestore屏障:对于这样的语句store1; storestore; store2,在store2及后续写入操作执行前,保证store1的写入操作对其它处理器可见。(也就是说如果出现storestore屏障,那么store1指令一定会在store2之前执行,CPU不会store1与store2进行重排序)
storeload屏障:对于这样的语句store1; storeload; load2,在load2及后续所有读取操作执行前,保证store1的写入对所有处理器可见。(也就是说如果出现storeload屏障,那么store1指令一定会在load2之前执行,CPU不会对store1与load2进行重排序)
volatile读的内存语义如下:
在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
线程对变量读取的时候,要从主内存中读,而不是缓存。
读操作在后面连续插入两个内存屏障
volatile内存语义的实现总结
volatile重排序规则表
总结起来就是:
1.当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。
2.当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。
3.当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后。
为什么要插入两个内存屏障:
要结合内存屏障的作用来看,LoadLoad 主要用来读操作LoadStore主要用来写操作,volition后面可能又有读又有写,为了防止有着两个操作(读和写),就一口气同时插入这两个内存屏障了.(防止下面的读和写对volatile读进行重排序)