1. 程式人生 > >Java併發43:併發集合系列-序章

Java併發43:併發集合系列-序章

由於私人原因,暫時沒有太多時間用於併發集合型別的例項學習上面。

所以從本章開始,後續併發集合型別相關文章都是轉載文章,特此說明。

這些轉載文章的敘述角度各不相同,不過不影響我們通過這些文章對併發集合有一個初步的理解。

集合

程式設計,離不開資料結構

JDK提供了Java集合框架(Java Collections framework),它包括可以用來實現多種不同的資料結構的介面、類和演算法,如HaspMap、ArrayList等等。

我們在使用集合框架的時候,需要十分小心以保證其多執行緒的安全性,因為大多數集合類並沒有對併發訪問進行控制。

併發集合

為了解決這些集合框架造成的安全性問題,JDK逐漸提供了越來越多的併發集合型別

我們在併發環境中,使用這些併發集合,不會產生資料不一致的問題。

阻塞與非阻塞

JDK提供我們的併發集合型別,按照阻塞方式分為兩種:

  • 阻塞佇列
    • 包含新增操作:如果不能立即進行新增,則是因為集合已滿;執行該操作的執行緒將被阻塞,直到新增成功
    • 包含刪除操作:如果不能立即進行刪除,則是因為集合已空;執行該操作的執行緒將被阻塞,直到刪除成功
  • 非阻塞佇列
    • 包含新增操作:如果不能立即進行新增,則將返回null值或丟擲異常
    • 包含刪除操作:如果不能立即進行刪除,則將返回null值或丟擲異常

集合型別

JDK提供我們的併發集合型別,按照集合型別分為以下五種:

  • List 列表

    • CopyOnWriteArrayList :
      • 基於寫時複製(Copy-On-Write)思想實現。
      • 適用於讀多寫少的併發場景,如黑名單,白名單等等。
      • 併發讀的效能高併發寫的空間消耗大
      • 只能保證資料的最終一致性,而非實時一致性
      • 讀不會阻塞寫會阻塞
  • Set 集合

    • CopyOnWriteArraySet :
      • 基於寫時複製(Copy-On-Write)思想實現。
      • 適用於讀多寫少的併發場景,如黑名單,白名單等等。
      • 併發讀的效能高併發寫的空間消耗大
      • 只能保證資料的最終一致性,而非實時一致性
      • 讀不會阻塞寫會阻塞
    • ConcurrentSkipListSet :

      • 基於跳錶(SkipList)實現。
      • 跳錶(SkipList)可以用來替代紅黑樹,使用概率均衡技術,插入、刪除操作更簡單、更快。
      • 跳錶(SkipList)本質上是以空間換取時間
      • ConcurrentSkipListSet是通過ConcurrentSkipListMap實現的。
      • ConcurrentSkipListMap中的元素是key-value鍵值對,而ConcurrentSkipListSet只用到了前者中的key
  • 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的大小。
  • Deque 雙端佇列
    • ConcurrentLinkedDeque :
    • LinkedBlockingDeque :
      • 基於獨佔鎖ReenTratLock+連結串列阻塞雙向無界佇列
      • 兩個ReentrantLock的獨佔鎖,分別用來控制元素入隊加鎖和出隊加鎖
      • 不僅支援FIFO操作,而且也支援FILO操作。
      • size()方法和remove()方法在併發環境中較為精確
      • 實現原來LinkedBlockingQueue 類似
      • 如何實現執行緒安全volatile變數 + ReenTrantLock獨佔鎖
  • 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操作。
      • 應用例項生產者與消費者