1. 程式人生 > >聊聊Java中的併發佇列中 有界佇列和無界佇列的區別

聊聊Java中的併發佇列中 有界佇列和無界佇列的區別

本文主要總體的說一說各種併發佇列 
首先來一張全體照 
這裡寫圖片描述

從有界無界上分 
常見的有界佇列為

  • ArrayBlockingQueue 基於陣列實現的阻塞佇列
  • LinkedBlockingQueue 其實也是有界佇列,但是不設定大小時就時Integer.MAX_VALUE,內部是基於連結串列實現的
  • ArrayBlockingQueue 與 LinkedBlockingQueue 對比一哈 
    • ArrayBlockingQueue 實現簡單,表現穩定,新增和刪除使用同一個鎖,通常效能不如後者
    • LinkedBlockingQueue 新增和刪除兩把鎖是分開的,所以競爭會小一些
  • SynchronousQueue 比較奇葩,內部容量為零,適用於元素數量少的場景,尤其特別適合做交換資料用,內部使用 佇列來實現公平性的排程,使用棧來實現非公平的排程,在Java6時替換了原來的鎖邏輯,使用CAS代替了
  • 上面三個佇列他們也是存在共性的 
    • put take 操作都是阻塞的
    • offer poll 操作不是阻塞的,offer 佇列滿了會返回false不會阻塞,poll 佇列為空時會返回null不會阻塞
    • 補充一點,並不是在所有場景下,非阻塞都是好的,阻塞代表著不佔用CPU,在有些場景也是需要阻塞的,put take 存在必有其存在的必然性

常見的無界佇列

  • ConcurrentLinkedQueue 無鎖佇列,底層使用CAS操作,通常具有較高吞吐量,但是具有讀效能的不確定性,弱一致性——不存在如ArrayList等集合類的併發修改異常,通俗的說就是遍歷時修改不會拋異常
  • PriorityBlockingQueue 具有優先順序的阻塞佇列
  • DelayedQueue 延時佇列,使用場景 
    • 快取:清掉快取中超時的快取資料
    • 任務超時處理
    • 補充:內部實現其實是採用帶時間的優先佇列,可重入鎖,優化阻塞通知的執行緒元素leader
  • LinkedTransferQueue 簡單的說也是進行執行緒間資料交換的利器,在SynchronousQueue 中就有所體現,並且併發大神 Doug Lea 對其進行了極致的優化,使用15個物件填充,加上本身4位元組,總共64位元組就可以避免快取行中的偽共享問題,其實現細節較為複雜,可以說一下大致過程: 
    • 比如消費者執行緒從一個佇列中取元素,發現佇列為空,他就生成一個空元素放入佇列 , 所謂空元素就是資料項欄位為空。然後消費者執行緒在這個欄位上旅轉等待。這叫保留。直到一個生產者執行緒意欲向隊例中放入一個元素,這裡他發現最前面的元素的資料項欄位為 NULL,他就直接把自已資料填充到這個元素中,即完成了元素的傳送。大體是這個意思,這種方式優美了完成了執行緒之間的高效協作。參考自 
      https://blog.csdn.net/u013851082/article/details/70140728
  • 現在也來說一說無界佇列的共同點 
    • put 操作永遠都不會阻塞,空間限制來源於系統資源的限制
    • 底層都使用CAS無鎖程式設計