線程安全集合

線程安全集合 簡介 JDK 1.2 中引入的 Collection 框架是一種表示對象集合的高度靈活的框架,它使用基本接口 List、Set 和 Map。通過 JDK 提供每個集合的多次實現(HashMap、Hashtable、TreeMap、WeakHashMap、HashSet、TreeSet、Vector、ArrayList、LinkedList 等等)。其中一些集合已經是線程安全的(Hashtable 和 Vector),通過同步的封裝工廠(Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()),其餘的集合均可表現爲線程安全的。 java.util.concurrent 包添加了多個新的線程安全集合類(ConcurrentHashMap、CopyOnWriteArrayList 和 CopyOnWriteArraySet)。這些類的目的是提供高性能、高度可伸縮性、線程安全的基本集合類型版本。 java.util 中的線程集合仍有一些缺點。例如,在迭代鎖定時,通常需要將該鎖定保留在集合中,否則,會有拋出 ConcurrentModificationException 的危險。(這個特性有時稱爲條件線程安全;有關的更多說明,請參閱 參考資料。)此外,如果從多個線程頻繁地訪問集合,則常常不能很好地執行這些類。java.util.concurrent 中的新集合類允許通過在語義中的少量更改來獲得更高的併發。 JDK 5.0 還提供了兩個新集合接口 —— Queue 和 BlockingQueue。Queue 接口與 List 類似,但它只允許從後面插入,從前面刪除。通過消除 List 的隨機訪問要求,可以創建比現有 ArrayList 和 LinkedList 實現性能更好的 Queue 實現。因爲 List 的許多應用程序實際上不需要隨機訪問,所以Queue 通常可以替代 List,來獲得更好的性能。 弱一致的迭代器 java.util 包中的集合類都返回 fail-fast 迭代器,這意味着它們假設線程在集合內容中進行迭代時,集合不會更改它的內容。如果 fail-fast 迭代器檢測到在迭代過程中進行了更改操作,那麼它會拋出 ConcurrentModificationException,這是不可控異常。 在迭代過程中不更改集合的要求通常會對許多併發應用程序造成不便。相反,比較好的是它允許併發修改並確保迭代器只要進行合理操作,就可以提供集合的一致視圖,如 java.util.concurrent 集合類中的迭代器所做的那樣。 java.util.concurrent 集合返回的迭代器稱爲弱一致的(weakly consistent)迭代器。對於這些類,如果元素自從迭代開始已經刪除,且尚未由 next() 方法返回,那麼它將不返回到調用者。如果元素自迭代開始已經添加,那麼它可能返回調用者,也可能不返回。在一次迭代中,無論如何更改底層集合,元素不會被返回兩次。 CopyOnWriteArrayList 和 CopyOnWriteArraySet 可以用兩種方法創建線程安全支持數據的 List —— Vector 或封裝 ArrayList 和 Collections.synchronizedList()。java.util.concurrent 包添加了名稱繁瑣的 CopyOnWriteArrayList。爲什麼我們想要新的線程安全的 List 類?爲什麼 Vector 還不夠? 最簡單的答案是與迭代和併發修改之間的交互有關。使用 Vector 或使用同步的 List 封裝器,返回的迭代器是 fail-fast 的,這意味着如果在迭代過程中任何其他線程修改 List,迭代可能失敗。 Vector 的非常普遍的應用程序是存儲通過組件註冊的監聽器的列表。當發生適合的事件時,該組件將在監聽器的列表中迭代,調用每個監聽器。爲了防止 ConcurrentModificationException,迭代線程必須複製列表或鎖定列表,以便進行整體迭代,而這兩種情況都需要大量的性能成本。 CopyOnWriteArrayList 類通過每次添加或刪除元素時創建支持數組的新副本,避免了這個問題,但是進行中的迭代保持對創建迭代器時的當前副本進行操作。雖然複製也會有一些成本,但是在許多情況下,迭代要比修改多得多,在這些情況下,寫入時複製要比其他備用方法具有更好的性能和併發性。 如果應用程序需要 Set 語義,而不是 List,那麼還有一個 Set 版本 —— CopyOnWriteArraySet。 ConcurrentHashMap 正如已經存在線程安全的 List 的實現,您可以用多種方法創建線程安全的、基於 hash 的 Map —— Hashtable,並使用 Collections.synchronizedMap() 封裝 HashMap。JDK 5.0 添加了 ConcurrentHashMap 實現,該實現提供了相同的基本線程安全的 Map 功能,但它大大提高了併發性。 Hashtable 和 synchronizedMap 所採取的獲得同步的簡單方法(同步 Hashtable 中或者同步的 Map 封裝器對象中的每個方法)有兩個主要的不足。首先,這種方法對於可伸縮性是一種障礙,因爲一次只能有一個線程可以訪問 hash 表。同時,這樣仍不足以提供真正的線程安全性,許多公用的混合操作仍然需要額外的同步。雖然諸如 get() 和 put() 之類的簡單操作可以在不需要額外同步的情況下安全地完成,但還是有一些公用的操作序列,例如迭代或者 put-if-absent(空則放入),需要外部的同步,以避免數據爭用。 Hashtable 和 Collections.synchronizedMap 通過同步每個方法獲得線程安全。這意味着當一個線程執行一個 Map 方法時,無論其他線程要對 Map 進行什麼樣操作,都不能執行,直到第一個線程結束纔可以。 對比來說,ConcurrentHashMap 允許多個讀取幾乎總是併發執行,讀和寫操作通常併發執行,多個同時寫入經常併發執行。結果是當多個線程需要訪問同一 Map 時,可以獲得更高的併發性。 在大多數情況下,ConcurrentHashMap 是 Hashtable或 Collections.synchronizedMap(new HashMap()) 的簡單替換。然而,其中有一個顯著不同,即 ConcurrentHashMap 實例中的同步不鎖定映射進行獨佔使用。實際上,沒有辦法鎖定 ConcurrentHashMap 進行獨佔使用,它被設計用於進行併發訪問。爲了使集合不被鎖定進行獨佔使用,還提供了公用的混合操作的其他(原子)方法,如 put-if-absent。ConcurrentHashMap 返回的迭代器是弱一致的,意味着它們將不拋出 ConcurrentModificationException ,將進行“合理操作”來反映迭代過程中其他線程對 Map 的修改。 隊列 原始集合框架包含三個接口:List、Map 和 Set。List 描述了元素的有序集合,支持完全隨即訪問 —— 可以在任何位置添加、提取或刪除元素。 LinkedList 類經常用於存儲工作元素(等待執行的任務)的列表或隊列。然而,List 提供的靈活性比該公用應用程序所需要的多得多,這個應用程序通常在後面插入元素,從前面刪除元素。但是要支持完整 List 接口則意味着 LinkedList 對於這項任務不像原來那樣有效。Queue 接口比 List 簡單得多,僅包含 put() 和 take() 方法,並允許比 LinkedList 更有效的實現。 Queue 接口還允許實現來確定存儲元素的順序。ConcurrentLinkedQueue 類實現先進先出(first-in-first-out,FIFO)隊列,而 PriorityQueue 類實現優先級隊列(也稱爲堆),它對於構建調度器非常有用,調度器必須按優先級或預期的執行時間執行任務。 interface Queue extends Collection { boolean offer(E x); E poll(); E remove() throws NoSuchElementException; E peek(); E element() throws NoSuchElementException; } 實現 Queue 的類是: • LinkedList 已經進行了改進來實現 Queue。 • PriorityQueue 非線程安全的優先級對列(堆)實現,根據自然順序或比較器返回元素。 • ConcurrentLinkedQueue 快速、線程安全的、無阻塞 FIFO 隊列。 BlockingQueue Queues 可以是受限制的,也可以是不受限制的。當試圖向已滿的隊列中添加元素時,或者當試圖從空隊列中刪除元素時,嘗試修改受限制的隊列將會失敗。 有時,當隊列操作可能會失敗時,您可能更願意是該失敗造成線程阻塞。除了不需要調用類來處理失敗和重試,阻塞還具有流控制的優點,如果消費者從對列刪除元素的速度比生產者向隊列放入元素的速度慢,則強制生產者阻塞將會抑制生產者。將這與不受限制的、無阻塞隊列形成對比,如果生產者和消費者之間的不平衡長期存在,系統可能會將內存用完,因爲隊列長度會無限制地增長。受限制的、阻塞隊列允許您以合理自然的方式限制給定隊列使用的資源。 實現 BlockingQueue 的類有: • LinkedBlockingQueue 向鏈接列表一樣實現的受限制或不受限制的 FIFO 阻塞隊列。 • PriorityBlockingQueue 不受限制的阻塞優先級隊列。 • ArrayBlockingQueue 數組支持的受限制 FIFO 阻塞隊列。 • SynchronousQueue 不是真正的隊列,但方便了互操作線程之間的同步傳遞。

發佈了32 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章