聊聊Java中的併發佇列中 有界佇列和無界佇列的區別
阿新 • • 發佈:2019-01-05
本文主要總體的說一說各種併發佇列
首先來一張全體照
從有界無界上分
常見的有界佇列為
- 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,他就直接把自已資料填充到這個元素中,即完成了元素的傳送。大體是這個意思,這種方式優美了完成了執行緒之間的高效協作。參考自
- 比如消費者執行緒從一個佇列中取元素,發現佇列為空,他就生成一個空元素放入佇列 , 所謂空元素就是資料項欄位為空。然後消費者執行緒在這個欄位上旅轉等待。這叫保留。直到一個生產者執行緒意欲向隊例中放入一個元素,這裡他發現最前面的元素的資料項欄位為 NULL,他就直接把自已資料填充到這個元素中,即完成了元素的傳送。大體是這個意思,這種方式優美了完成了執行緒之間的高效協作。參考自
- 現在也來說一說無界佇列的共同點
- put 操作永遠都不會阻塞,空間限制來源於系統資源的限制
- 底層都使用CAS無鎖程式設計