Java併發環境下的佇列(Queue)概述
本文作者:王一飛,叩丁狼高階講師。原創文章,轉載請註明出處。
概念
佇列
佇列是一種特殊的線性表,是一種先進先出(FIFO)的資料結構。它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。佇列中沒有元素時,稱為空佇列。正因為佇列先進先出的先天特性,在一些特殊場合下稱為首選項,比如:電商的秒殺。將每一個訂單請求作為一個處理任務,按順序一一排列到佇列中等待處理。經典運用:執行緒池。
java 中的佇列(Queue)
Queue介面與List、Set為同級別,都為Collection介面子介面。除了擁有 Collection 介面基本操作外,佇列還提供其他的插入、提取和檢查操作。每個方法都存在兩種形式:一種丟擲異常(操作失敗時),另一種返回一個特殊值(null 或 false,具體取決於操作)。插入操作的後一種形式是用於專門為有容量限制的 Queue 實現設計的;在大多數實現中,插入操作不會失敗。
Queue 大體分2類:
1>非阻塞佇列: 沒有實現BlockingQueue介面的佇列:
常見的有:
ConcurrentLinkedQueue:一個基於連結串列節點的無界執行緒安全佇列。此佇列按照 FIFO(先進先出)原則對元素進行排序。佇列的頭部 是佇列中時間最長的元素。佇列的尾部 是佇列中時間最短的元素。新的元素插入到佇列的尾部,佇列獲取操作從佇列頭部獲得元素。當多個執行緒共享訪問一個公共 collection 時,ConcurrentLinkedQueue 是一個恰當的選擇。
LinkedList:底層維護一個連結串列,實現所有可選的列表操作,並且允許所有元素(包括 null)。除了實現 List 介面外,LinkedList 類還為在列表的開頭及結尾 get、remove 和 insert 元素提供了統一的命名方法。可用於實現堆疊、佇列或雙端佇列等。
PriorityQueue:一個基於優先順序堆的無界優先順序佇列。優先順序佇列的元素按照其自然順序進行排序,或者根據構造佇列時提供的 Comparator 進行排序,具體取決於所使用的構造方法。
2>阻塞佇列:實現BlockingQueue介面的佇列
常見有:
ArrayBlockingQueue:一個由陣列支援的有界阻塞佇列。此佇列按 FIFO(先進先出)原則對元素進行排序。佇列的頭部 是在佇列中存在時間最長的元素。佇列的尾部 是在佇列中存在時間最短的元素。新元素插入到佇列的尾部,佇列獲取操作則是從佇列頭部開始獲得元素。因為底層快取的一有界的陣列,當快取區建立成功後,長度便固定,試圖向滿佇列中放元素,勢必導致阻塞。另外,此類也支援公平原則,如果公平引數被設定true,等待時間最長的執行緒會優先得到處理。公平性通常會降低吞吐量,但也減少了可變性和避免了“不平衡性”。
LinkedBlockingQueue:底層使用連結串列實現的無上界的阻塞佇列(可以使用指定界限的方式,設定佇列的容量大小, 預設為 Integer.MAX_VALUE。),通常連結串列佇列的的吞吐量要高於基於陣列的佇列,但是在大多數併發應用程式中,其可預知的效能要低。
DelayQueue:一個無界阻塞佇列,和其他佇列不同,獲取該佇列資料,只有在延遲期滿時才能從中提取元素。該佇列的頭部是延遲期滿後儲存時間最長的 Delayed 元素。如果延遲都還沒有期滿,則佇列沒有頭部,並且poll將返回null。當一個元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個小於等於 0 的值時,將發生到期。即使無法使用 take 或 poll 移除未到期的元素,也不會將這些元素作為正常元素對待。
PriorityBlockingQueue:是一個帶優先順序的 佇列,而不是先進先出佇列。元素按優先順序順序被移除,該佇列也沒有上限(看了一下原始碼,PriorityBlockingQueue是對 PriorityQueue的再次包裝,是基於堆資料結構的,而PriorityQueue是沒有容量限制的,與ArrayList一樣,所以在優先阻塞 佇列上put時是不會受阻的。雖然此佇列邏輯上是無界的,但是由於資源被耗盡,所以試圖執行新增操作可能會導致 OutOfMemoryError),但是如果佇列為空,那麼取元素的操作take就會阻塞,所以它的檢索操作take是受阻的。另外,往入該佇列中的元 素要具有比較能力。
SynchronousQueue:一種阻塞佇列,其中每個插入操作必須等待另一個執行緒的對應移除操作 ,反之亦然。同步佇列沒有任何內部容量,甚至連一個佇列的容量都沒有。不能在同步佇列上進行 peek,因為僅在試圖要移除元素時,該元素才存在;除非另一個執行緒試圖移除某個元素,否則也不能(使用任何方法)插入元素;也不能迭代佇列,因為其中沒有元素可用於迭代。佇列的頭 是嘗試新增到佇列中的首個已排隊插入執行緒的元素;如果沒有這樣的已排隊執行緒,則沒有可用於移除的元素並且 poll() 將會返回 null。對於其他 Collection 方法(例如 contains),SynchronousQueue 作為一個空 collection。此佇列不允許 null 元素。
本篇到這,僅僅介紹java中各種Queue(詳細參考jdk),具體使用,下篇繼續。