内存屏障是什么?

内存屏障,也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

内存屏障为硬件层的概念,不同的操作系统实现内存屏障的手段不一样,java通过jvm来统一生成内存屏障指令。

内存屏障有什么用?

  • 禁止屏障两侧的指令进行重排序;
  • 强制将缓冲区、高速缓存区的数据等写回主内存,让缓存相对应的数据失效;

内存屏障有哪几种?

硬件层

硬件层的内存屏障分为两种:LoadBarriers和StoreBarriers,即读屏障和写屏障。

LoadBarriers:在指令前插入LoadBarriers,可以让缓存中的数据失效,强制从主存加载数据;

StoreBarriers:在指令后插入StoreBarriers,可以让写入缓存的数据更新写入主存,对其他线程可见。

Java内存屏障

java内存屏障分为四种(为硬件层内存屏障的组合情况):LoadLoad,StoreStore,LoadStore,StoreLoad,借此完成一系列的屏障和数据同步:

  • LoadLoad:load1;LoadLoad;load2;在load2及之后要读取的数据被访问之前,保证load1要读取的数据已经被读取完毕;
  • StoreStore:store1;StoreStore;store2;在store2及之后的写操作执行之前,保证store1的写入操作对所有处理器可见;
  • LoadStore:load1;LoadStore;store2;在store2及之后的写操作执行之前,保证load1要读取的数据已经被读取完毕;
  • StoreLoad:store1;StoreLoad;load2;在load2及之后要读取的数据被访问之前,保证store1的写入对所有处理器可见。(开销最大,在大多数处理器的实现中,该屏障为万能屏障,包含其他三种屏障的功能)

Java内存屏障应用

Volatile语义中的内存屏障

Volatile的内存屏障采用“悲观”的态度来使用内存屏障

  • 在volatile修饰的变量写操作之前插入StoreStore屏障,在写操作之后插入StoreLoad屏障,以确保该变量写入的值对其他线程可见
  • 在volatile修饰的变量读操作之前插入LoadLoad屏障,在读操作之后插入LoadStore屏障

由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性这块理不清????。

final语义中的内存屏障

对于final域,编译器和CPU会遵循两个排序规则:

  • 新建对象过程中,构造体中对final域的初始化写入和这个对象赋值给其他引用变量,这两个操作不能重排序;
  • 初次读包含final域的对象引用和读取这个final域,这两个操作不能重排序;(意思就是先赋值引用,再调用final值)

必需保证一个对象的所有final域被写入完毕后才能引用和读取。这也是内存屏障的起的作用:

写final域:在编译器写final域完毕,构造体结束之前,会插入一个StoreStore屏障,保证前面的对final写入对其他线程/CPU可见,并阻止重排序。

读final域:在上述规则2中,两步操作不能重排序的机理就是在读final域前插入了LoadLoad屏障。