//一個volatile的用例
//Thread A
init();
initlized= true;
//Thread B
while(!initlized){
sleep();
}
//volatile變量的作用如下,保證了線程B使用initlized變量時,從
主線程讀取,而不是使用工作線程的變量(變量可見性)。保證了線程A
中的initlized=true會在init()方法後執行(禁止指令重排序)。
volatile關鍵字的語義主要是以下兩個:
- 變量可見:一個變量的修改(assigned+store+write)馬上可以被其他線程觀察到。
- 禁止指令重排序:initlized=true必須在init()之後執行。重點內容
volatile語義的實現:
- 可見性只要強制要求read-load-use操作必須一起出現以及assign-store-write操作必須一起出現即可(一個volatile變量的assign操作要馬上同步回主內存,一個use操作需要先從主內存加載數據)。
- 禁止指令重排序是通過內存屏障實現的,內存屏蔽是類似於lock add 0x0 esp之類的指令,通過在init()與initlized=true之間使用內存屏蔽指令,強制要求initlized=true必須在init()之後執行。
volatile的用處
- 對共享變量x的修改不依賴與x的值,或只有一個線程修改x。
- 變量不需要與其他狀態變量一起參與不變約束。
volatile+不可變類可以提供線程安全的變量訪問,因爲對於變量引用的變化可以立即被其他線程觀察到,而引用的對象狀態是不變的。
volatile+非原子性操作都不是線程安全的,因爲不滿足互斥性。
如if(x != 8) x = 9;或x++;之類的操作,因爲在load+use指令之後(將x存入寄存器),線程可能掛起,然後由其他線程對x的值進行變更,此時線程切回原線程就可能使用了歷史數據,而導致數據錯誤。
volatile最合適的使用方法就是第一個例子,用於單線程X設置一個狀態變量,利用其可見性以及禁止指令重排序的特點,將X的工作狀態及時的通知給其他線程。另外一個用法就是單例模式中的DCL方法(雙鎖檢測)。
volatile A instance = null;
A getInstance(){
if(instance==null){
synchronized(A.class){
//在這裏使用了volatile的可見性,防止重複創建實例。
if(instance == null){
instance = new A();
}
}
}
return instance;
}