參考文章:
https://www.cnblogs.com/dolphin0520/p/3920373.html
當(dāng)一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存赖晶,當(dāng)有其他線程需要讀取時诉儒,它會去內(nèi)存中讀取新值。
1.volatile關(guān)鍵字的兩層語義
一旦一個共享變量(類的成員變量淆衷、類的靜態(tài)成員變量)被volatile修飾之后缸榄,那么就具備了兩層語義:
1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值祝拯,這新值對其他線程來說是立即可見的甚带。
2)禁止進行指令重排序。
volatile能保證原子性嗎?
volatile也無法保證對變量的任何操作都是原子性的鹰贵。volatile能保證語句順序嗎晴氨?
只能保證volatile之后的語句不會先執(zhí)行。
舉例:
//x碉输、y為非volatile變量
//flag為volatile變量
x = 2; //語句1
y = 0; //語句2
flag = true; //語句3
x = 4; //語句4
y = -1; //語句5
1籽前、2順序不管,但 3不會在1腊瑟、2之前執(zhí)行聚假,也不會在4、5之后執(zhí)行闰非。
4.volatile的原理和實現(xiàn)機制
前面講述了源于volatile關(guān)鍵字的一些使用膘格,下面我們來探討一下volatile到底如何保證可見性和禁止指令重排序的。
下面這段話摘自《深入理解Java虛擬機》:
“觀察加入volatile關(guān)鍵字和沒有加入volatile關(guān)鍵字時所生成的匯編代碼發(fā)現(xiàn)财松,加入volatile關(guān)鍵字時瘪贱,會多出一個lock前綴指令”
lock前綴指令實際上相當(dāng)于一個內(nèi)存屏障(也成內(nèi)存柵欄),內(nèi)存屏障會提供3個功能:
1)它確保指令重排序時不會把其后面的指令排到內(nèi)存屏障之前的位置辆毡,也不會把前面的指令排到內(nèi)存屏障的后面菜秦;即在執(zhí)行到內(nèi)存屏障這句指令時,在它前面的操作已經(jīng)全部完成舶掖;
2)它會強制將對緩存的修改操作立即寫入主存球昨;
3)如果是寫操作,它會導(dǎo)致其他CPU中對應(yīng)的緩存行無效眨攘。
- 使用場景
(1)狀態(tài)量標(biāo)記
volatile boolean flag = false;
while(!flag){
doSomething();
}
public void setFlag() {
flag = true;
}
volatile boolean inited = false;
//線程1:
context = loadContext();
inited = true;
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
(2)雙重檢查double-check
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}