volatile关键字详解

Posted by KANG's BLOG on Tuesday, March 15, 2022

作用

一个被volatile声明的变量主要有以下两种特性保证保证线程安全

  • 可见性 将当前处理器缓存行的数据会写回到系统内存。 这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效
  • 有序性 虚拟机在进行代码编译优化的时候,对于那些改变顺序之后不会对最终变量的值造成影响的代码,volatile可防止编译器将他们进行重排序。
int a = 10;
int b = 5;

原理

被volatile关键字修饰的变量会存在一个"lock:“的前缀

Lock前缀,Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁

在具体的执行上,它先对总线和缓存加锁,然后执行后面的指令,在Lock锁住总线的时候,其他CPU的读写请求都会被阻塞,直到锁释放。最后释放锁后会把高速缓存中的脏数据全部刷新回主内存,且这个写回内存的操作会使在其他CPU里缓存了该地址的数据无效

Lock只是完成类似内存屏障的功能,但不是内存屏障

但是volatile确实增加了内存屏障:

  • 每个volatile写操作前插入StoreStore屏障(这个屏障前后的2个Store指令不能交换顺序),在写操作后插入StoreLoad屏障(这个屏障前后的2个Store Load指令不能交换顺序)
  • 每个volatile读操作前插入LoadLoad屏障(这个屏障前后的2个Load指令不能交换顺序),在读操作后插入LoadStore屏障(这个屏障前后的2个Load Store指令不能交换顺序)

MESI(缓存一致协议)

M(Modified)

这行数据有效,缓存数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。

E(Exclusive)

这行数据有效,缓存数据和内存中的数据一致,数据只存在于本Cache中。

S(Shared)

这行数据有效,缓存数据和内存中的数据一致,数据存在于很多Cache中。

I(Invalid)

缓存数据无效。

非线程安全

volatile不能保证原子性,比如

i++

该操作分为读取计算和写回,所以该操作并非原子的,volatile并不用对于属性提供原子性。