作用
一个被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并不用对于属性提供原子性。