第七十條 執行緒安全性的文件化
當一個類的方法或靜態方法被併發使用的時候,很可能會造成執行緒安全隱患。如果你沒有在一個類的文件中描述其行為的併發性情況,使用這個類的其他人將不得不做出某些demo來驗證它是否併發安全,可能缺少足夠的同步,或者過度同步,無論屬於這其中的哪種情況,都可能會發生嚴重的錯誤。我們可能聽過這樣的說法:通過檢視文件中是否出現synchronized修飾符,可以確定一個方法是否是執行緒安全的。這種說法是錯誤的,在正常的操作中,Javadoc並沒有在它的輸出中包含synchronized修飾符,因為在一個方法宣告中出現synchronized修飾符,這是個實現細節,並不是匯出的API的一部分。它並不一定表明這個方法是執行緒安全的。
“出現了synchronized關鍵字就足以用文件說明執行緒安全性”的這種說法隱含了一個錯誤的觀念,即認為執行緒安全性是一種“要麼全有要麼全無”的屬性。實際上,執行緒安全性有多種級別。一個類為了可被多個執行緒安全的使用,必須在文件中清楚地說明他所支援的執行緒安全性級別。
不可變的(immutable)——這個類的例項是不變的。所以,不需要外部的同步。這樣的例子包括String、Long和BigInteger。
無條件的執行緒安全(unconditionally thread-safe)——這個類的例項是可變的,但是這個類有足夠的內部同步,所以,它的例項可以被併發使用,無需任何外部同步。其例子包括Random和ConcurrentHashMap。
有條件的執行緒安全(conditionally thread-safe)——除了有些方法為進行安全的併發使用而需要外部同步之外,這種執行緒安全級別與無條件的執行緒安全相同。這樣的例子包括Collections.synchronized包裝返回的集合,他們的迭代器要求外部同步。
非執行緒安全(not thread-safe)——這個類的例項是可變的。為了併發的使用他們,客戶必須利用自己選擇的外部同步包圍每個方法呼叫(或者呼叫序列)。這樣的例子包括通用的集合實現,例如ArrayList和HashMap。
執行緒對立的(thread-hostile)——這個類不能安全的被多個執行緒併發使用,即使所有方法呼叫都被外部同步包圍。執行緒對立的根源通常在於,沒有同步的修改靜態資料。沒有人會有意編寫一個執行緒對立的類;這種類是因為沒有考慮到併發性而產生的後果。幸運的是,在Java平臺類庫中,執行緒對立的類或方法非常少。System.runFinalizersOnExit方法是執行緒對立的,但已經被廢除了。最後一條基本可以忽略不計。
每個類都應該利用字斟句酌的說明或者執行緒安全註解,清楚地在文件中說明他的執行緒安全屬性。sychronized修飾符與這個文件毫無關係。有條件的執行緒安全類必須在文件中指明“哪個方法呼叫序列需要外部同步,以及在執行這些序列的時候要獲得哪把鎖”。如果你編寫的是無條件的執行緒安全類,就應該考慮使用私有鎖物件來代替同步的方法。這樣可以防止客戶端程式和子類的不同步干擾,讓你能夠在後續的版本中靈活的對併發控制採用更加複雜的方法。
這一條我也理解不是很到位。總之,在併發情況下,如果想讓東西初始化一次,要麼把它寫成單利,要麼對方法加鎖,或者可以用 String 或 BigInteger ,如果初始化過一次,就賦一個值,或者讓本身加1,判斷它的值是否初始化,我們最終還是要善用方法,完成功能。