參考原文: http://www.importnew.com/18126.html
基本是對原文的精簡, 推薦閱讀原文以獲得詳細(xì)講解.
volatile能夠保證值在修改時會立即更新到主存. 當(dāng)其他線程讀取時讀到的一定是最新的
volatile關(guān)鍵字和java內(nèi)存模型有關(guān), 所以先了解下內(nèi)存模型
簡述:
cpu運(yùn)行速度遠(yuǎn)快于對內(nèi)存的讀寫, 為了加快速度, 增加了高速緩存. 而一旦增加緩存, 就會出現(xiàn)緩存一致性問題.
解決思路:
1. 在總線上加Lock;
2. 通過緩存一致性協(xié)議;
早期使用思路1解決問題, 但是這樣的話其他cpu無法訪問內(nèi)存, 效率低下.
所以出現(xiàn)了緩存一致性協(xié)議, 最出名的就是Intel的MESI協(xié)議.
核心思路:
當(dāng)cpu寫數(shù)據(jù)的時候, 如果發(fā)現(xiàn)操作的變量是共享變量, 即在其他cpu中也存在該變量的副本, 則發(fā)出信號通知其他cpu將該變量的緩存行置為無效狀態(tài). 讓他們重新從內(nèi)存中讀取最新值.
并發(fā)編程常見問題:
1.原子性問題;
2. 可見性問題; 一個線程修改了變量, 其他線程能夠立馬看到修改的值.
3. 有序性問題;
有序性
虛擬機(jī)--指令重排序
//好例子
//線程1:
context = loadContext(); //語句1
inited = true; //語句2
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
語句1和語句2可能被重排, 導(dǎo)致線程2讀取inited為true. 結(jié)果加載配置的時候卻沒加載到....
指令重排序不會影響單線程的執(zhí)行, 但是會影響線程并發(fā)執(zhí)行的正確性.
java內(nèi)存模型
java內(nèi)存模型規(guī)定所有的變量都存在主存當(dāng)中, 每個線程有自己的工作內(nèi)存, 線程對變量的操作都必須在工作內(nèi)存中進(jìn)行, 而不能直接對主存進(jìn)行操作, 并且每個線程不能訪問其他線程的工作內(nèi)存.
x = 10; //語句1 **原子性**
y = x; //語句2 **不是原子性** 包含兩個動作: 1.讀取x的值; 2.寫入工作內(nèi)存;(注意工作內(nèi)存四個字)
x++; //語句3 **不是原子性** 三步: 讀-改-寫
x = x + 1; //語句4 **不是原子性** 三步: 讀-改-寫
在32位平臺上, 保存64位數(shù)據(jù)會分成兩節(jié). 導(dǎo)致不能保證原子性(現(xiàn)在據(jù)說已經(jīng)實(shí)現(xiàn)原子性了?)...但是不論如何, 加上volatile關(guān)鍵字是良好的編程規(guī)范.
synchronized和lock自然也能保證可見性. 只是性能上沒有volatile更優(yōu). 請因地制宜, 合理使用.
volatile的兩層含義
- 保證了不同線程對這個變量操作時的可見性;
- 禁止進(jìn)行指令重排序;
"禁止指令重排序"的實(shí)際使用
//線程1:
context = loadContext(); //語句1
volatile inited = true; //語句2 加上volatile,保證語句2在語句1之后執(zhí)行.
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
volatile保證可見性, 卻無法保證原子性.
使用volatile關(guān)鍵字的兩個條件:
- 對變量的寫操作不依賴于當(dāng)前值;
- 該變量沒有包含在具有其他變量的不變式中;
下面列舉幾個Java中使用volatile的幾個場景。
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;
}
}