Java中volatile的併發安全性分析

一、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


                                       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章