Java併發43:併發集合系列-序章
阿新 • • 發佈:2019-02-04
由於私人原因,暫時沒有太多時間用於併發集合型別的例項學習上面。
所以從本章開始,後續併發集合型別相關文章都是轉載文章,特此說明。
這些轉載文章的敘述角度各不相同,不過不影響我們通過這些文章對併發集合有一個初步的理解。
集合
程式設計,離不開資料結構。
JDK提供了Java集合框架(Java Collections framework),它包括可以用來實現多種不同的資料結構的介面、類和演算法,如HaspMap、ArrayList等等。
我們在使用集合框架的時候,需要十分小心以保證其多執行緒的安全性,因為大多數集合類並沒有對併發訪問進行控制。
併發集合
為了解決這些集合框架造成的安全性問題,JDK逐漸提供了越來越多的併發集合型別 。
我們在併發環境中,使用這些併發集合,不會產生資料不一致的問題。
阻塞與非阻塞
JDK提供我們的併發集合型別,按照阻塞方式分為兩種:
- 阻塞佇列
- 包含新增操作:如果不能立即進行新增,則是因為集合已滿;執行該操作的執行緒將被阻塞,直到新增成功。
- 包含刪除操作:如果不能立即進行刪除,則是因為集合已空;執行該操作的執行緒將被阻塞,直到刪除成功。
- 非阻塞佇列
- 包含新增操作:如果不能立即進行新增,則將返回null值或丟擲異常。
- 包含刪除操作:如果不能立即進行刪除,則將返回null值或丟擲異常。
集合型別
JDK提供我們的併發集合型別,按照集合型別分為以下五種:
- List 列表
- CopyOnWriteArrayList :
- 基於寫時複製(Copy-On-Write)思想實現。
- 適用於讀多寫少的併發場景,如黑名單,白名單等等。
- 併發讀的效能高;併發寫的空間消耗大。
- 只能保證資料的最終一致性,而非實時一致性。
- 讀不會阻塞,寫會阻塞。
- CopyOnWriteArrayList :
Set 集合
- CopyOnWriteArraySet :
- 基於寫時複製(Copy-On-Write)思想實現。
- 適用於讀多寫少的併發場景,如黑名單,白名單等等。
- 併發讀的效能高;併發寫的空間消耗大。
- 只能保證資料的最終一致性,而非實時一致性。
- 讀不會阻塞,寫會阻塞。
- ConcurrentSkipListSet :
- 基於跳錶(SkipList)實現。
- 跳錶(SkipList)可以用來替代紅黑樹,使用概率均衡技術,插入、刪除操作更簡單、更快。
- 跳錶(SkipList)本質上是以空間換取時間。
- ConcurrentSkipListSet是通過ConcurrentSkipListMap實現的。
- ConcurrentSkipListMap中的元素是key-value鍵值對,而ConcurrentSkipListSet只用到了前者中的key。
- CopyOnWriteArraySet :
Map 對映
- ConcurrentSkipListMap :
- 基於跳錶(SkipList)實現。
- 跳錶(SkipList)可以用來替代紅黑樹,使用概率均衡技術,插入、刪除操作更簡單、更快。
- 跳錶(SkipList)本質上是以空間換取時間。
- ConcurrentSkipListSet是通過ConcurrentSkipListMap實現的。
- ConcurrentSkipListMap中的元素是key-value鍵值對,而ConcurrentSkipListSet只用到了前者中的key。
- ConcurrentHashMap :
- 基於所使用的鎖分段技術實現,減小鎖粒度,減少資料競爭的概率。
- get操作高效之處在於整個get過程不需要加鎖,除非讀到的值是空的才會加鎖重讀。
- get操作的高效的根本原因在於採用了volatile關鍵字來保持多執行緒可見性。
- put操作之前首先需要進行Segment加鎖;但不會影響其他Segment鎖。
- put操作造成的擴容時,不是對整個容器進行雙倍擴容,而只對某個segment進行雙倍擴容,節省了大量空間。
- size操作:先嚐試2次通過不鎖住Segment的方式來統計各個Segment大小,如果統計的過程中,容器的count發生了變化,則再採用加鎖的方式來統計所有Segment的大小。
- ConcurrentSkipListMap :
- Deque 雙端佇列
- ConcurrentLinkedDeque :
- LinkedBlockingDeque :
- 基於獨佔鎖ReenTratLock+連結串列的阻塞雙向無界佇列。
- 有兩個ReentrantLock的獨佔鎖,分別用來控制元素入隊加鎖和出隊加鎖。
- 不僅支援FIFO操作,而且也支援FILO操作。
- size()方法和remove()方法在併發環境中較為精確。
- 實現原來:LinkedBlockingQueue 類似。
- 如何實現執行緒安全:volatile變數 + ReenTrantLock獨佔鎖。
- ConcurrentLinkedDeque :
- Queue 佇列
- ConcurrentLinkedQueue :
- 基於非阻塞CAS演算法的非阻塞單向無界列表。
- 只支援FIFO操作。
- size()方法和contains()方法在併發環境中並不精確。
- 實際應用:Tomcat中NioEndPoint。
- 如何實現執行緒安全:volatile變數(head,tail)保證可見性,CAS操作保證原子性和有序性。
- LinkedBlockingQueue :
- 基於獨佔鎖ReenTratLock+連結串列的阻塞單向無界佇列。
- 有兩個ReentrantLock的獨佔鎖,分別用來控制元素入隊加鎖和出隊加鎖。
- 只支援FIFO操作。
- size()方法和remove()方法在併發環境中較為精確。
- 實際應用:tomcat中任務佇列TaskQueue。
- 如何實現執行緒安全:volatile變數 + ReenTrantLock獨佔鎖。
- ArrayBlockingQueue :
- 基於獨佔鎖ReenTratLock+陣列的阻塞單向有界佇列。
- 只有一個全域性ReentrantLock的獨佔鎖,用來控制元素入隊加鎖和出隊加鎖。
- 只支援FIFO操作。
- size()方法和remove()方法在併發環境中較為精確。
- 如何實現執行緒安全:ReenTrantLock全域性獨佔鎖。
- PriorityBlockingQueue :
- 基於獨佔鎖ReenTratLock+二叉樹最小堆的阻塞單向無界優先順序佇列。
- 只有一個全域性ReentrantLock的獨佔鎖,用來控制元素入隊加鎖和出隊加鎖。
- size()方法在併發環境中精確的。
- 如何實現執行緒安全:ReenTrantLock全域性獨佔鎖。
- DelayQueue :
- 基於獨佔鎖ReenTratLock+優先順序阻塞佇列PriorityBlockingQueue的阻塞單向無界延時佇列。
- 最先過期的元素具有最高優先順序。
- 只有一個全域性ReentrantLock的獨佔鎖,用來控制元素入隊加鎖和出隊加鎖。
- 如何實現執行緒安全:ReenTrantLock全域性獨佔鎖。
- 主要用途:快取佇列和超時任務處理。
- SynchronousQueue :
- 基於CAS操作的非阻塞無資料緩衝佇列。
- 不存在資料空間:佇列頭元素是第一個排隊要插入資料的執行緒,而不是要交換的資料。
- 因為沒有資料空間,很多方法不可用:peek、clear、contains、isEmpty、size、remove等等。
- 應用場景:生產者的執行緒和消費者的執行緒同步以傳遞某些資訊、事件或者任務。
- 應用例項:Executors.newCachedThreadPool()。
- 可以這樣來理解:生產者和消費者互相等待對方,握手,然後一起離開。
- LinkedTransferQueue :
- 基於預佔模式+連結串列的單向阻塞無界佇列。
- 預佔模式:有就拿貨走人,沒有就佔個位置等著,等到或超時。
- transfer演算法採用所謂雙重資料結構(dual data structures):保留與完成。
- 只支援FIFO操作。
- 應用例項:生產者與消費者。
- ConcurrentLinkedQueue :