初识内存屏障

内存屏障的概念来自硬件层,硬件层的内存屏障分为两种:Load Barrier 和 Store Barrier即读屏障和写屏障。

  • 内存屏障有两个作用:
  1. 阻止屏障两侧的指令重排序;
  2. 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。
  • 对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据;

  • 对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见。

我们把Load就是我们内存操作上的读,Store就是写

Java的内存屏障有四种,实际上就是读和写的组合,来完成一系列的屏障和数据同步功能

读-读屏障:

Load1;
LoadLoad;
Load2;

保证Load1要在Load2及以后的操作之前先完成读取的操作

写-写屏障:

Store1;
StoreStore;
Store2;

保证Store1的写操作要在Store2及以后之前对其他处理器可见(线程)

读-写屏障:

Load1;
LoadStore;
Store2;

保证Load1要在Store2及以后之前先完成读取的操作-

写-读屏障:

Store1;
StoreLoad;
Load2;

保证Store1的写操作要在Load2及以后之前要对其他处理器可见(线程)

衍生知识

  1. Volatile的内存屏障策略

    特点:严格保守,悲观

    在每个volatile写操作前插入写-写屏障,在写操作后插入写-读屏障;

    在每个volatile读操作前插入读-读屏障,在读操作后插入读-写屏障;

    有效防止Volatile变量及其后续指令的重排序、线程之间实现安全的通信,正式因为这样,被Volatile修饰过的变量便显现出了锁的特性

  2. Final的内存屏障

    在新建对象过程中,后遭提对final的初始化写入和这个对赋值给其他引用变量不能重排序

    final域对象的赋值引用和调用final值不能重排序

    必须保证final域写入后才能引用和读取

    写final域之前会插入写-写屏障保障final域的可见而且防止重排序

    读final域就是之前插入读-读屏障实现防止重排序

    x86处理器会对读-读、写-写忽略

尾巴

Java的并发与内存屏障息息相关,内存屏障保障了并发的线程安全问题,弄懂底层实现能更好去理解Java的并发思想,所以才有了这篇文章,如有错漏,还望批评指导