轉載:http://benjaminwhx.com/2018/05/05/%E8%AF%B4%E8%AF%B4%E9%98%9F%E5%88%97Queue/前端
一、簡介
Queue(隊列):一種特殊的線性表,它只容許在表的前端(front)進行刪除操做,只容許在表的後端(rear)進行插入操做。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。後端
每一個元素老是從隊列的rear端進入隊列,而後等待該元素以前的全部元素出隊以後,當前元素才能出對,遵循先進先出(FIFO)原則。數組
下面是Queue類的繼承關係圖:緩存
![](http://static.javashuo.com/static/loading.gif)
圖中咱們能夠看到,最上層是Collection接口,Queue知足集合類的全部方法:併發
- add(E e):增長元素;
- remove(Object o):刪除元素;
- clear():清除集合中全部元素;
- size():集合元素的大小;
- isEmpty():集合是否沒有元素;
- contains(Object o):集合是否包含元素o。
二、隊列
2.一、Queue
Queue:隊列的上層接口,提供了插入、刪除、獲取元素這3種類型的方法,並且對每一種類型都提供了兩種方式,先來看看插入方法:app
- add(E e):插入元素到隊尾,插入成功返回true,沒有可用空間拋出異常 IllegalStateException。
- offer(E e): 插入元素到隊尾,插入成功返回true,不然返回false。
add和offer做爲插入方法的惟一不一樣就在於隊列滿了以後的處理方式。add拋出異常,而offer返回false。ide
再來看看刪除和獲取元素方法(和插入方法相似):函數
- remove():獲取並移除隊首的元素,該方法和poll方法的不一樣之處在於,若是隊列爲空該方法會拋出異常,而poll不會。
- poll():獲取並移除隊首的元素,若是隊列爲空,返回null。
- element():獲取隊列首的元素,該方法和peek方法的不一樣之處在於,若是隊列爲空該方法會拋出異常,而peek不會。
- peek():獲取隊列首的元素,若是隊列爲空,返回null。
若是隊列是空,remove和element方法會拋出異常,而poll和peek返回null。this
固然,Queue只是單向隊列,爲了提供更強大的功能,JDK在1.6的時候新增了一個雙向隊列Deque,用來實現更靈活的隊列操做。spa
2.二、Deque
Deque在Queue的基礎上,增長了如下幾個方法:
- addFirst(E e):在前端插入元素,異常處理和add同樣;
- addLast(E e):在後端插入元素,和add同樣的效果;
- offerFirst(E e):在前端插入元素,異常處理和offer同樣;
- offerLast(E e):在後端插入元素,和offer同樣的效果;
- removeFirst():移除前端的一個元素,異常處理和remove同樣;
- removeLast():移除後端的一個元素,和remove同樣的效果;
- pollFirst():移除前端的一個元素,和poll同樣的效果;
- pollLast():移除後端的一個元素,異常處理和poll同樣;
- getFirst():獲取前端的一個元素,和element同樣的效果;
- getLast():獲取後端的一個元素,異常處理和element同樣;
- peekFirst():獲取前端的一個元素,和peek同樣的效果;
- peekLast():獲取後端的一個元素,異常處理和peek同樣;
- removeFirstOccurrence(Object o):從前端開始移除第一個是o的元素;
- removeLastOccurrence(Object o):從後端開始移除第一個是o的元素;
- push(E e):和addFirst同樣的效果;
- pop():和removeFirst同樣的效果。
能夠發現,其實不少方法的效果都是同樣的,只不過名字不一樣。好比Deque爲了實現Stack的語義,定義了push和pop兩個方法。
三、阻塞隊列
3.一、BlockingQueue
BlockingQueue(阻塞隊列),在Queue的基礎上實現了阻塞等待的功能。它是JDK 1.5中加入的接口,它是指這樣的一個隊列:當生產者向隊列添加元素但隊列已滿時,生產者會被阻塞;當消費者從隊列移除元素但隊列爲空時,消費者會被阻塞。
先給出BlockingQueue新增的方法:
- put(E e):向隊尾插入元素。若是隊列滿了,阻塞等待,直到被中斷爲止。
- boolean offer(E e, long timeout, TimeUnit unit):向隊尾插入元素。若是隊列滿了,阻塞等待timeout個時長,若是到了超時時間尚未空間,拋棄該元素。
- take():獲取並移除隊首的元素。若是隊列爲空,阻塞等待,直到被中斷爲止。
- poll(long timeout, TimeUnit unit):獲取並移除隊首的元素。若是隊列爲空,阻塞等待timeout個時長,若是到了超時時間尚未元素,則返回null。
- remainingCapacity():返回在無阻塞的理想狀況下(不存在內存或資源約束)此隊列能接受的元素數量,若是該隊列是無界隊列,返回Integer.MAX_VALUE。
- drainTo(Collection<? super E> c):移除此隊列中全部可用的元素,並將它們添加到給定 collection 中。
- drainTo(Collection<? super E> c, int maxElements):最多今後隊列中移除給定數量的可用元素,並將這些元素添加到給定 collection 中。
BlockingQueue最重要的也就是關於阻塞等待的幾個方法,而這幾個方法正好能夠用來實現生產-消費的模型。
從圖中咱們能夠知道實現了BlockingQueue的類有如下幾個:
- ArrayBlockingQueue:一個由數組結構組成的有界阻塞隊列。
- LinkedBlockingQueue:一個由鏈表結構組成的有界阻塞隊列。
- PriorityBlockingQueue:一個支持優先級排序的無界阻塞隊列。
- SynchronousQueue:一個不存儲元素的阻塞隊列。
- DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
ArrayBlockingQueue
ArrayBlockingQueue是一個用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。默認狀況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的全部生產者線程或消費者線程,當隊列可用時,能夠按照阻塞的前後順序訪問隊列,即先阻塞的生產者線程,能夠先往隊列裏插入元素,先阻塞的消費者線程,能夠先從隊列裏獲取元素。一般狀況下爲了保證公平性會下降吞吐量。咱們可使用如下代碼建立一個公平的阻塞隊列:
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000, true);
View Code
訪問者的公平性是使用可重入鎖實現的,代碼以下:
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
View Code
LinkedBlockingQueue
LinkedBlockingQueue是一個用鏈表實現的有界阻塞隊列。此隊列的默認和最大長度爲Integer.MAX_VALUE。此隊列按照先進先出的原則對元素進行排序。
PriorityBlockingQueue
PriorityBlockingQueue是一個支持優先級的無界隊列。默認狀況下元素採起天然順序排列,也能夠經過比較器comparator來指定元素的排序規則。元素按照升序排列。
SynchronousQueue
SynchronousQueue是一個不存儲元素的阻塞隊列。每個put操做必須等待一個take操做,不然不能繼續添加元素。SynchronousQueue能夠當作是一個傳球手,負責把生產者線程處理的數據直接傳遞給消費者線程。隊列自己並不存儲任何元素,很是適合於傳遞性場景,好比在一個線程中使用的數據,傳遞給另一個線程使用,SynchronousQueue的吞吐量高於LinkedBlockingQueue 和 ArrayBlockingQueue。
DelayQueue
DelayQueue是一個支持延時獲取元素的無界阻塞隊列。隊列使用PriorityQueue來實現。隊列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才能從隊列中提取元素。咱們能夠將DelayQueue運用在如下應用場景:
- 緩存系統的設計:能夠用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。
- 定時任務調度。使用DelayQueue保存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,從好比TimerQueue就是使用DelayQueue實現的。
3.二、BlockingDeque
BlockingDeque(阻塞雙端隊列)在Deque的基礎上實現了雙端阻塞等待的功能。和第2節說的相似,BlockingDeque也提供了雙端隊列該有的阻塞等待方法:
- putFirst(E e):在隊首插入元素,若是隊列滿了,阻塞等待,直到被中斷爲止。
- putLast(E e):在隊尾插入元素,若是隊列滿了,阻塞等待,直到被中斷爲止。
- offerFirst(E e, long timeout, TimeUnit unit):向隊首插入元素。若是隊列滿了,阻塞等待timeout個時長,若是到了超時時間尚未空間,拋棄該元素。
- offerLast(E e, long timeout, TimeUnit unit):向隊尾插入元素。若是隊列滿了,阻塞等待timeout個時長,若是到了超時時間尚未空間,拋棄該元素。
- takeFirst():獲取並移除隊首的元素。若是隊列爲空,阻塞等待,直到被中斷爲止。
- takeLast():獲取並移除隊尾的元素。若是隊列爲空,阻塞等待,直到被中斷爲止。
- pollFirst(long timeout, TimeUnit unit):獲取並移除隊首的元素。若是隊列爲空,阻塞等待timeout個時長,若是到了超時時間尚未元素,則返回null。
- pollLast(long timeout, TimeUnit unit):獲取並移除隊尾的元素。若是隊列爲空,阻塞等待timeout個時長,若是到了超時時間尚未元素,則返回null。
- removeFirstOccurrence(Object o):從隊首開始移除第一個和o相等的元素。
- removeLastOccurrence(Object o):從隊尾開始移除第一個和o相等的元素。
從圖中咱們能夠知道實現了BlockingDeque的類有:
- LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
3.三、TransferQueue
TransferQueue是JDK 1.7對於併發類庫新增長的一個接口,它擴展自BlockingQueue,因此天然保持着阻塞隊列的全部特性。
有人這樣評價它:TransferQueue是是ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、無界的LinkedBlockingQueues等的超集。
TransferQueue對比與BlockingQueue更強大的一點是,生產者會一直阻塞直到所添加到隊列的元素被某一個消費者所消費(不只僅是添加到隊列裏就完事)。新添加的transfer方法用來實現這種約束。顧名思義,阻塞就是發生在元素從一個線程transfer到另外一個線程的過程當中,它有效地實現了元素在線程之間的傳遞(以創建Java內存模型中的happens-before關係的方式)。
咱們來看看該接口提供的標準方法:
- tryTransfer(E e):若當前存在一個正在等待獲取的消費者線程(使用take()或者poll()函數),使用該方法會即刻轉移/傳輸對象元素e並當即返回true;若不存在,則返回false,而且不進入隊列。這是一個不阻塞的操做。
- transfer(E e):若當前存在一個正在等待獲取的消費者線程,即馬上移交之;不然,會插入當前元素e到隊列尾部,而且等待進入阻塞狀態,到有消費者線程取走該元素。
- tryTransfer(E e, long timeout, TimeUnit unit):若當前存在一個正在等待獲取的消費者線程,會當即傳輸給它;不然將插入元素e到隊列尾部,而且等待被消費者線程獲取消費掉;若在指定的時間內元素e沒法被消費者線程獲取,則返回false,同時該元素被移除。
- hasWaitingConsumer():判斷是否存在消費者線程。
- getWaitingConsumerCount():獲取全部等待獲取元素的消費線程數量。
其實transfer方法在SynchronousQueue的實現中就已存在了,只是沒有作爲API暴露出來。SynchronousQueue有一個特性:它自己不存在容量,只能進行線程之間的元素傳送。SynchronousQueue在執行offer操做時,若是沒有其餘線程執行poll,則直接返回false.線程之間元素傳送正是經過transfer方法完成的。
TransferQueue相比SynchronousQueue用處更廣、更好用,由於你能夠決定是使用BlockingQueue的方法(例如put方法)仍是確保一次傳遞完成(即transfer方法)。在隊列中已有元素的狀況下,調用transfer方法,能夠確保隊列中被傳遞元素以前的全部元素都能被處理。
從圖中咱們能夠知道實現了TransferQueue的類有:
- LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
好了,隊列的API先說到這裏,下面我會另起一文重點說說阻塞隊列的那些個實現類的原理。