逐一解释一下四个 “内存屏障” 是什么

什么是内存屏障?硬件层⾯,内存屏障分两种:读屏障(Load Barrier)和写屏障(Store Barrier)。内存屏障有两个作⽤:

  1. 阻⽌屏障两侧的指令重排序;
  2. 强制把写缓冲区/⾼速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效。

注意这⾥的缓存主要指的是CPU缓存,如L1,L2等(这里所提到的缓存是指CPU缓存,例如L1,L2等级别的缓存。这是因为在计算机系统中,还存在其他类型的缓存,比如磁盘缓存、网络缓存等等。因此,这个主句的作用是强调内存屏障所涉及到的缓存类型,避免混淆。)

编译器在⽣成字节码时,会在指令序列中插⼊内存屏障来禁⽌特定类型的处理器重排序。编译器选择了⼀个⽐较保守的JMM内存屏障插⼊策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:

  1. 在每个volatile写操作前插⼊⼀个StoreStore屏障;
  2. 在每个volatile写操作后插⼊⼀个StoreLoad屏障;
  3. 在每个volatile读操作后插⼊⼀个LoadLoad屏障;
  4. 在每个volatile读操作后再插⼊⼀个LoadStore屏障。

示意图⼤概是这个样⼦:

逐一解释一下四个 “内存屏障” 是什么_第1张图片

LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写⼊操作执⾏前,保证Store1的写⼊操作对其它处理器可⻅。

LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写⼊操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执⾏前,保证Store1的写⼊对所有处理器可⻅。它的开销是四种屏障中最⼤的(冲刷写缓冲器,清空⽆效化队列)。在⼤多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

再介绍⼀下volatile与普通变量的重排序规则:

  1.  如果第⼀个操作是volatile读,那⽆论第⼆个操作是什么,都不能重排序;
  2. 如果第⼆个操作是volatile写,那⽆论第⼀个操作是什么,都不能重排序;
  3. 如果第⼀个操作是volatile写,第⼆个操作是volatile读,那不能重排序。

如果是下列情况:第⼀个操作是普通变量读,第⼆个操作是volatile变量读,那是可以重排序的:

// 声明变量
int a = 0; // 声明普通变量
volatile boolean flag = false; // 声明volatile变量
// 以下两个变量的读操作是可以重排序的
int i = a; // 普通变量读
boolean j = flag; // volatile变量读

你可能感兴趣的:(并发,缓存)