浅解volatile

volatile是Java的关键字,有三个特性:可见性、有序性、原子性。

可见性:对一个volatile的读,总能看到任意线程对这个volatile写的最后的写入

原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种符合操作不具有原子性。

有序性:底层利用了内存屏障来实现了指令重排序。

当一个被volatile修饰的共享变量进行写操作的时候,底层会用Lock前缀去修饰。

  • 将当前处理器缓存行的数据写回到系统内存
  • 写回内存的操作会让其他CPU里缓存了该内存地址的数据无效,然后缓存一致性

在多处理器的情况下,为了让各个处理器的缓存是一致的,就会实现缓存一致性协议。每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是否过期,档处理器发现自己缓存行的内存地址被修改后,就会将当前处理器的缓存设置为无效状态,当处理器对这个数进行修改指令的时候,就会重新从系统中的内存去把数据读取到处理器的缓存里。

volatile的禁止指令重拍的实现是因为volatile的内存语义的实现

在执行程序时为了提高性能,编译器和处理器通常会对指令做重排序:

  1. 编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序;
  2. 处理器重排序。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;

内存屏障是一组处理指令,用来实现对内存操作的顺序限制。volatile的底层就是通过内存屏障来实现的。

内存屏障就是禁止屏障两边的指令互相穿越

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

  • 对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据;
  • 对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见。

作用:

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

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写操作前插入StoreStore屏障

在每个volatile写操作后插入StoreLoad屏障

在每个volatile读操作前插入LoadLoad屏障

在每个volatile读操作后插入LoadStore屏障

猜你喜欢

转载自blog.csdn.net/OrangeRawNorthland/article/details/83617404
今日推荐