標籤:重要 加鎖 問題 數組 hashmap 並發 登出 寫入 ash
同步容器將所有對容器狀態的訪問都序列化,以實現執行緒安全性。這種方式的缺點是嚴重降低並發性。Java 5.0提供了多種並發容器來改進同步容器的效能。如ConcurrentHashMap代替同步且基於散列的Map,CopyOnWriteArrayList,用於在遍曆操作主要操作的情況下代替同步的List,同時提供了一些常見的符合操作,如,若沒有則添加,替換及有條件刪除等。Java 6引入了ConcurrentSckipListMap和ConcurrentSkipListSet,分別代替同步的SortedMap和SortedSet。
通過並發容器來代替同步容器,可以極大地提高效能並降低風險。
ConcurrentHashMap
同步容器類在執行每個操作期間都持有鎖。在一些操作,如HashMap.get和List.contains,可能包含大量工作:遍曆散列表或鏈表來尋找特定的對象,必須調用equals方法。這將花費大量時間,而其他線程在這段時間將不能訪問該容器。
ConcurrentHashMap 也是基於散列的Map,並不是將每個方法都在同一個鎖上同步並使每次只有一個線程訪問容器,而是使用一種粒度更細的加鎖機制來實現各大程度的共用,這種機制稱為分段鎖。任意數量的讀取線程可以並發的訪問Map,執行讀取操作的線程和執行寫入操作的線程可以並發的訪問Map,並且一定數量的寫入線程可以並發的修改Map。
類似的並發容器增強了同步容器,它們提供的迭代器不會拋出ConcurrentModeficationException,因此不需要在迭代過程加鎖。 ConcurrentHashMap 返回的迭代器具有弱一致性,可以容忍並發的修改,當建立迭代器時會遍曆已有的元素,並可以在迭代器被構造後將修改操作反映給容器。
儘管並發容器改進很多,但是還存在需要權衡的因素,對於需要在整個map上計算的方法,如size和isEmpty,這些方法返回的結果可能已經到期了,實際上只是估計值。雖然這個看上去存在很大問題,實際上在並發環境下,這些方法作用很小,因為他們的值一直在變。這些操作的弱化換取其他更重要操作的效能最佳化,如get,put,containsKey和remove等。
與Hashtable和synchronizedMap相比, ConcurrentHashMap有著更多的優勢及更少的劣勢,因此在大多數情況下使用 ConcurrentHashMap代替同步Map能進一步提高代碼的延展性。只有需要加鎖Map進行獨佔訪問時才應該放棄 ConcurrentHashMap。
由於 ConcurrentHashMap不能被加鎖來執行獨佔訪問,因此無法使用用戶端加鎖建立新的原子操作,但是一些常見的複合操作已經實現為原子操作。
CopyOnWriteArrayList
CopyOnWriteArrayList 用於替代同步List,在某些情況下提供更好的並發效能。並且迭代期間不需要加鎖或者複製。執行緒安全性在於,“寫入時複”制,每次修改時都會建立並重新發布一個新的容器副本。“寫入時複製”容器的迭代器保留一個指向底層基礎數組的引用,這個數組位於迭代器的起始位置,由於不會被修改,因此同步時只需確保數組內容的可見度。多個線程可以對這個容器進行迭代。容器的迭代器返回的元素與迭代器建立時的元素完全一致。
每當修改容器時都會複製底層數組,這需要一定的開銷,特別是規模較大時。僅當迭代操作遠大於修改操作時,才應該使用"寫入時複製"容器。類似的還有CopyOnWriteArraySet。如用於事件通知系統,註冊和登出事件的操作遠少於接受事件的操作。
java並發容器