目录
目录
Volatile关键字如何保证可见性?如何实现有序性?
一、lock前缀指令+MESI缓存一致性协议
对Volatile关键字修饰的变量,执行写操作的时候,Jvm会发送一条lock前缀指令给CPU,CPU在操作完这个变量后会立即把这个值写会主存中,同时有MESI缓存一致性协议,所以各个CPU都会对总线进行嗅探,感知自己本地高速缓存区中的数据是否被其他线程修改过。如果发现本地缓存的数据被其他线程修改,那么本地高速缓存区与主存中不一致的数据当做过期的值并清理掉。然后再从主存内中重新加载最新的数据。
二、内存屏障:禁止指令重排
内存屏障类型:
1、LoadLoad屏障
load1;loadload;load2; 确保load1数据的加载顺序优先于load2以及其后所有的装载指令,即Load1对应的代码指令与Load2对应的代码指令是不能进行指令重排。
例:
Load1:
localFlag=this.variable;
LaodLoad屏障:
Load2:
localFlag=this.variable2;
2、StoreStore屏障
store1;storestore;store2; 确保store1的数据优先于store2以及其后所有的数据刷入主存,即Load1对应的代码指令与Load2对应的代码指令是不能进行指令重排。
例:
Store1:
this.variable=1;
StoreStore屏障:
Store2:
this.variable=2;
3、LoadStore屏障
load1;LoadStore;Store2; 确保load1数据的加载顺序优先于Store2以及其后所有的指令
例:
Load1:
localFlag=this.variable;
LoadStore屏障:
Store2:
this.variable=2;
4、StoreLoad屏障
Store1;StoreLoad;load2; 确保Store1数据刷入主存,并且对其他cpu可见,且优先于load2以及其后所有的装载指令。
例:
Store1:
this.variable=2;
StoreLoad屏障:
Load2:
localFlag=this.variable2;
三、Volatile屏障总结:
每个Volatile写操作前加上StoreStore屏障,禁止上面的普通写和它重排;
每个Volatile写操作后面加StoreLoad屏障,禁止跟下面的Volatile读/写重排;
每个Volatile读操作前面加LoadLoad屏障,禁止跟下面的普通读和Volatile读重排;
每个Volatile读操作后面加LoadStore屏障,禁止禁止下面的普通写和Volatile读重排;