java併發和高併發編程之線程不安全類與寫法

一、線程不安全類與寫法:

1、相對於不可變對象和線程封閉帶來的線程安全,什麼是線程不安全類呢?

  如果一個類的對象同時可以被多個線程訪問,如果不做特殊的同步或者併發處理,那麼它就很容易表現出線程不安全的現象。比如,拋出異常、邏輯處理錯誤等等。這種類被稱爲線程不安全類。

2、最常見的一些線程不安全類:

》StringBuilder(線程不安全) ——》StringBuffer(線程安全)

運行結果如下:

按照預期,我們期望最後的結果是5000,現在看到,結果小於它,運行多次均如此,事實證明該類是不安全的。

那,如果將StringBuilder換成StringBuffer,再次運行,會發現,結果每次都是5000,符合預期,再次證明後者是線程安全的。

查看對比源碼容易看到,StringBuffer 中,使用了synchronized關鍵字。該類內部好多方法都用了該關鍵字修飾。

對比,如果是在局部變量內部,使用StringBuilder類,線程相對安全,性能高。

   多線程環境,考慮安全問題時,使用StringBuffer

》SimpleDateFormat ——》JodaTime

 

 

 運行結果:

如上,報出異常,而且多次運行,結果穩定出現bug.

不過,針對SimpleDateFormat  ,如上可以進行優化,優化如下方案進行:

將聲明爲SimpleDateFormat類 對象從全局變量變化爲局部變量,即將全局變量那一行移到update()方法中。

 經過運行,即可發現該方案是正確的。

》JodaTime 類,不是java原生的包中有的。

如上,運行可以發現,數字每次最終會出現4999,即符合預期,是個線程安全的類。

順便附上該類所在包導入的方法:

》ArrayList , HashSet , HashMap 等Collections

因爲平常都用爲局部變量,一般很少觸發線程不安全問題。但是如果定義爲static時,遇到多線程,則很容易觸發多線程安全問題。

》》測試一:ArrayList

 運行結果如下,

如上,期望值應該是5000,但返回值經常小於它,充分說明,ArrayList是個線程不安全的 。

那麼,HashSet 結果如何呢?在此,將ArrayList換成HashSet 試試看

》測試二:HashSet :

如上,經過多次運行,結果也是小於5000,那如果換成hashMap 呢?

》》測試三:HashMap 

如果按照預期,它應該也是5000纔對,但運行結果

卻發現,它也是線程不安全的。

總結:經過上方測試,如上,最常用的ArrayList  HashSet  HashMap 都是線程不安全的。

關於它們對應的線程安全類,後面介紹。說明,有很多。

3、線程不安全的寫法:先檢查再執行:

   if  (condition(a)  ) {

          handle(a);

    }

解釋:針對上述寫法,假設a是個線程安全的類,但是在多線程中,分別處理時,如果在接隙過程中,操作不是原子性的,就存在多線程安全問題。所以,在用到判斷某個條件是否符合滿足某個條件,滿足某個條件再進行某個操作時,一定要先確定該對象是否有可能多個線程共享。如果是多線程共享的,一定要加個鎖或者保證兩個操作是原子性的,否則是有可能觸發線程不安全的。

 

 

 

 

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