Java並發(一)-了解線程安全
阿新 • • 發佈:2018-08-20
println 發現 java並發編程 發的 for 混合 private 和我 情況
線程不安全性
先來舉例說明線程不安全是什麽情況下發生的:例如一個變量可以被多個線程進行訪問,那麽在大量線程並發訪問這個變量的情況下,線程執行的順序會給最後的結果帶來不可預估的錯誤。
先定義一個單例類SimpleWorkingHardSingleton:
public class SimpleWorkingHardSingleton { private static SimpleWorkingHardSingleton simpleSingleton = new SimpleWorkingHardSingleton(); // 數量 private int count; private SimpleWorkingHardSingleton() { count = 0; } public static SimpleWorkingHardSingleton getInstance() { return simpleSingleton; } public int getCount() { return count; } public void addCount(int increment) { this.count += increment; System.out.println(this.count); } }
可以看到下面這個單例若在多線程環境下運行,count是被多個線程同時操縱的變量,示例:
for (int i = 0; i < 5; i++) { new Thread(new Runnable() { @Override public void run() { SimpleWorkingHardSingleton simpleSingleton = SimpleWorkingHardSingleton.getInstance(); simpleSingleton.addCount(1); } }).start(); }
看輸出結果(你的執行結果可能和我的不同):
3
2
2
4
5
匪夷所思的結果,想不懂為什麽會有兩個2出現,1哪兒去了,為什麽輸出不是 1 2 3 4 5,下面就來解釋一下
- 為什麽沒有1?
可能是a線程算出count=1,然後輸出count的時候,此時a線程掛起,b線程執行,b線程對count自增,此時a線程再輸出的時候,count已經發生了變化,這就導致了1沒有被輸出 - 為什麽兩個2?
可能是a,b兩個線程都完成了count的計算,然後a線程輸出,輸出結束後立即被掛起,然後緊接著b線程立即也進行了輸出,那麽此時a b線程一定是輸出了相同的count,就導致了相同值的出現。 - 將循環次數加大到100或者200,就會發現最後輸出的count並不會到100或者200
這是由於count++這個操作也不是原子的,也就是說count++並不是一次性完成,而是分為3步驟。第一步,獲取count;第二步,給count指向的值加1;第三步,講計算結果寫回count。因此如果三個步驟再混合上多線程執行順序的錯亂因素,就會導致不可預測的問題了。比如a線程獲取count為1,此時a線程立馬被掛起,b線程獲取count也為1,然後a,b線程各自去執行,最後寫回count都是2,這就導致了count被少加一次。
線程安全性
其實我們看到線程安全性的定義的關鍵點在於正確性,即在多線程的環境下,無論運行時候環境采用如何的調度方式,系統或者類或者方法總能表現出預期的相符的行為,那麽就是線程安全的。
總結
- 在多線程環境下,之所以會出現並發的線程安全性問題,是由於多個線程去操縱一個共享的變量或者一組變量,而且變量的操作過程不是原子的,那麽線程的執行順序就會幹擾到變量。
- 為了保證線程安全性,解決方法:
- 多個線程訪問一個不可變量
- 變量不可以被多線程共享
- 線程做同步處理(原子性處理)
參考內容
- 書籍《Java並發編程實戰》
Java並發(一)-了解線程安全