在Java類庫中出現的第一個關聯的集合類是Hashtable,它是JDK1.0的一部分。Hashtable提供了一種便於使用的、安全執行緒的、關聯的map功能,這當然也是方便的。然而,執行緒安全性是憑代價換來的――Hashtable的所有方法都是同步的。此時,無競爭的同步會導致可觀的效能代價。Hashtable的後繼者HashMap是作為JDK1.2中的集合架構的一部分出現的,它通過提供一個不同步的基類和一個同步的封裝器Collections.synchronizedMap,解決了執行緒安全性問題。通過將基本的功能從執行緒安全性中分離開來,Collections.synchronizedMap允許需要同步的使用者可以擁有同步,而不需要同步的使用者則不必為同步付出代價。
Hashtable和synchronizedMap所採取的獲得同步的簡單方法(同步Hashtable中或者同步的Map封裝器對象中的每個方法)有兩個主要的不足。首先,這種方法對於延展性是一種障礙,因為一次只能有一個線程可以訪問hash表。同時,這樣仍不足以提供真正的執行緒安全性,許多公用的混合操作仍然需要額外的同步。雖然諸如get()和put()之類的簡單操作可以在不需要額外同步的情況下安全地完成,但還是有一些公用的操作序列,例如迭代或者put-if-absent(空則放入),需要外部的同步,以避免資料爭用。
ConcurrentHashMap
util.concurrent包中的ConcurrentHashMap類(也將出現在JDK1.5中的java.util.concurrent包中)是對Map的安全執行緒的實現,比起synchronizedMap來,它提供了好得多的並發性。多個讀操作幾乎總可以並發地執行,同時進行的讀和寫操作通常也能並發地執行,而同時進行的寫操作仍然可以不時地並發進行(相關的類也提供了類似的多個讀線程的並發性,但是,只允許有一個活動的寫線程)。ConcurrentHashMap被設計用來最佳化檢索操作;實際上,成功的get()操作完成之後通常根本不會有鎖著的資源。要在不使用鎖的情況下取得執行緒安全性需要一定的技巧性,並且需要對Java記憶體模型(JavaMemoryModel)的細節有深入的理解。ConcurrentHashMap實現,加上util.concurrent包的其他部分,已經被研究正確性和執行緒安全性的並發專家所正視。在下個月的文章中,我們將看看ConcurrentHashMap的實現的細節。
ConcurrentHashMap通過稍微地鬆弛它對調用者的承諾而獲得了更高的並發性。檢索操作將可以返回由最近完成的插入操作所插入的值,也可以返回在步調上是並發的插入操作所添加的值(但是決不會返回一個沒有意義的結果)。由ConcurrentHashMap.iterator()返回的Iterators將每次最多返回一個元素,並且決不會拋出ConcurrentModificationException異常,但是可能會也可能不會反映在該迭代器被構建之後發生的插入操作或者移除操作。在對集合進行迭代時,不需要表範圍的鎖就能提供執行緒安全性。在任何不依賴於鎖整個表來防止更新的應用程式中,可以使用ConcurrentHashMap來替代synchronizedMap或Hashtable。
上述改進使得ConcurrentHashMap能夠提供比Hashtable高得多的延展性,而且,對於很多類型的公用案例(比如共用的cache)來說,還不用損失其效率。
CopyOnWriteArrayList
在那些遍曆操作大大地多於插入或移除操作的並發應用程式中,一般用CopyOnWriteArrayList類替代ArrayList。如果是用於存放一個接聽程式(listener)列表,例如在AWT或Swing應用程式中,或者在常見的JavaBean中,那麼這種情況很常見(相關的CopyOnWriteArraySet使用一個CopyOnWriteArrayList來實現Set介面)。
如果您正在使用一個普通的ArrayList來存放一個接聽程式列表,那麼只要該列表是可變的,而且可能要被多個線程訪問,您就必須要麼在對其進行迭代操作期間,要麼在迭代前進行的複製操作期間,鎖定整個列表,這兩種做法的開銷都很大。當對列表執行會引起列表發生變化的操作時,CopyOnWriteArrayList並不是為列表建立一個全新的副本,它的迭代器肯定能夠返回在迭代器被建立時列表的狀態,而不會拋出ConcurrentModificationException。在對列表進行迭代之前不必複製列表或者在迭代期間鎖定列表,因為迭代器所看到的列表的副本是不變的。換句話說,CopyOnWriteArrayList含有對一個不可變數組的一個可變的引用,因此,只要保留好那個引用,您就可以獲得不可變的執行緒安全性的好處,而且不用鎖定列表。
同步的集合類Hashtable和Vector,以及同步的封裝器類Collections.synchronizedMap和Collections.synchronizedList,為Map和List提供了基本的有條件的安全執行緒的實現。然而,某些因素使得它們並不適用於具有高度並發性的應用程式中――它們的集合範圍的單鎖特性對於延展性來說是一個障礙,而且,很多時候還必須在一段較長的時間內鎖定一個集合,以防止出現ConcurrentModificationExceptions異常。ConcurrentHashMap和CopyOnWriteArrayList實現提供了更高的並發性,同時還保住了執行緒安全性,只不過在對其調用者的承諾上打了點折扣。ConcurrentHashMap和CopyOnWriteArrayList並不是在您使用HashMap或ArrayList的任何地方都一定有用,但是它們是設計用來最佳化某些特定的公用解決方案的。許多並發應用程式將從對它們的使用中獲得好處。