Java併發編程實戰筆記(2):線程安全性-原子性

原子性

  • 競態關係
  • 複合操作

我們還是以一段代碼來理解這兩點。

public class ThreadSecStudy {
    static int count = 0;
    public static void main(String[] args) {
        // 正確性反例
        Thread add = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 50000; i++) {
                    count++;
                    System.out.println(count);
                }
            }
        });
        Thread cut = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 50000; i++) {
                    count--;
                    System.out.println(count);
                }
            }
        });

		// 加法
        add.start();
        // 減法
        cut.start();
    }
}

我們的類方法中對count的操作看似只有一行count++和count–,但是實際上它做了三步操作,獲取-修改-寫入,拆分開來可以是這樣寫。

//獲取
int tmpGet = count;
//修改
int tmpUpdate = count + 1;
//寫入
count = tmpUpdate;

那麼我們設想如果有多個線程同時對同一個變量做這三步操作,有沒有可能出現如下這種情況呢?

線程1爲加法,線程2爲減法。

  1. count = 0
  2. =》線程1加入
  3. 線程1:獲取操作 tmpGet = count = 0
  4. =》線程2加入
  5. 線程2:獲取操作 tmpGet = count = 0
  6. 線程1:修改操作 tmpUpdate = count + 1 = 1
  7. 線程2:修改操作 tmpUpdate = count - 1 = -1
  8. 線程1:寫入操作 count = tmpUpdate = 1
  9. 線程2:寫入操作 count = tmpUpdate = -1

很明顯-1這個結果是不對的,一加一減正確的結果應該是0纔是正確的,那麼這就出現了線程安全的問題了。

首先這個操作它屬於複合操作,它是一個可以被拆分的操作,和線程安全性中的體現原子性是恰恰相反的。

並且當一個計算的正確性取決於多個線程的交替執行時序時,就會發生競態條件


加深理解

如果你的操作屬於複合操作,那麼在被多個線程交替執行時序時,內部可能會發生競態條件,導致計算的結果正確性得不到保證,從而導致線程安全問題。
反之,如果確保了其操作的原子性,則不會發生競態條件;而要保證原子性則要提供互斥訪問,同一時刻只能有一個線程對數據進行操作


開發中如何做

最簡單的也最常見的方式就是我們使用synchronized的時候,我們將複合操作合併成爲一個符合原子性的操作,這樣就保證了操作的原子性。


文章中出現的任何錯誤歡迎指正,共同進步!

最後做個小小廣告,有對WEB開發和網絡安全感興趣的,可以加羣一起學習和交流!

交流羣
QQ:425343603

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