一、volatile的解釋
當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,它會去內存中讀取新值。
二、問題的發現
從volatile的定義上來看它看似應該能保證多個線程對volatile變量使用的同步,那麼先來看一段代碼。
package com;
public class Main {
public volatile static int count = 0;
public static void main(String[] args) throws Exception {
// 同時啓動1000個線程,去進行i++計算,看看實際結果
Thread threads[] = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
Main.count++;
}
});
threads[i].start();
}
// 主線程等待每個線程執行完成
for (int i = 0; i < 1000; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//結果如果出現不是1000,則出現了同步問題
System.out.println("運行結果:Counter.count=" + Main.count);
}
}
運行結果:Counter.count=998
三、分析和結論
從上面代碼的運行結果上來看,顯然voltile沒有達到我們想要的效果(讓couter=1000),那麼這是什麼原因呢?其實volatile只能保證緩存一致性,或者說併發編程中的可見性——即一個線程對volatile變量的修改會立即更新到主存,其他線程在使用該變量的時候會從主存讀取最新的值。
但是——————它不能保證原子性!!!
我們假設有A、B、C三個線程和volatile變量X=0
A——X=1
B——X=X+1
C——X=X+2;
設想如下執行情況,(1)A執行完,此時X=1 (2)B、C開始同時執行,從主存中讀取最新的值到工作內存
(3)B、C同時執行加運算(4)B寫入主存,C寫入主存
以上執行情況並沒有違反volatile的規定,因爲B、C同時讀到了最新的值,但是在B寫入主存的時候,C已經執行完加運算,不要讀取最新的X值了,因此連續兩次覆蓋寫入,導致了線程同步不理想。
那麼這裏的原子性沒有得到保證是指什麼呢?——讀和寫的操作不是原子性的!
更多參考資料如下:
http://www.cnblogs.com/dolphin0520/p/3920373.html
http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html