1.簡述
Queue用於模擬隊列這種數據結構,隊列通常是指先進先出(FIFO)的容器。新元素插入(offer)到隊列的尾部,訪問元素(poll)操作會返回隊列頭部的元素。通常,隊列不允許隨機訪問隊列中的元素。
Queue接口中有以下幾個常用實現類:
- PriorityQueue:非阻塞、非線程安全、無邊界,支持優先級隊列實現類。
- ConcurrentLinkedQueue:非阻塞、線程安全、無邊界,基於鏈接節點的隊列實現類。
- ArrayBlockingQueue:阻塞、線程安全、有邊界,一旦創建容量不可改變實現類。
- LinkedBlockingQueue:阻塞、線程安全、可選有邊界,一個由鏈表結構組成的可選有界阻塞隊列實現類,如果未指定容量,那么容量將等於
Integer.MAX_VALUE
。 - PriorityBlockingQueue:阻塞、線程安全、無邊界,支持優先級排序的無邊界阻塞隊列實現類。
- DelayQueue:阻塞、線程安全、無邊界,使用優先級隊列實現的無界阻塞隊列實現類,只有在延遲期滿時才能從中提取元素。
- SynchronousQueue:阻塞、線程安全、無數據隊列,不存儲元素、沒有內部容量的阻塞隊列實現類。
- LinkedBlockingDeque:阻塞、線程安全、無邊界,由鏈表結構組成的可選范圍雙向阻塞隊列實現類,如果未指定容量,那么容量將等於
Integer.MAX_VALUE
。
2.PriorityQueue
PriorityQueue即優先隊列,優先隊列的作用是能保證每次取出的元素都是隊列中權值最小的(優先隊列每次取最小元素)。這里牽涉到了大小關系,元素大小的評判可以通過元素本身的自然順序(natural ordering),也可以通過構造時傳入的比較器(Comparator)。
PriorityQueue不允許放入null
元素。其通過堆實現,具體說是通過完全二叉樹(complete binary tree)實現的小頂堆(任意一個非葉子節點的權值,都不大於其左右子節點的權值),也就意味着可以通過數組來作為PriorityQueue的底層實現。
定義一個PriorityQueue的方式有如下幾種:

//創建一個PriorityQueue隊列,初始化一個容量為11的且以自然順序排序元素的優先隊列 PriorityQueue<String> queue = new PriorityQueue<String>(); //創建一個PriorityQueue隊列,初始化指定大小的容量的優先隊列,且以自然順序排列元素 queue = new PriorityQueue<String>(30); //創建一個PriorityQueue隊列,包含collection queue = new PriorityQueue<String>(new ArrayList<String>()); //創建一個PriorityQueue隊列,初始化指定大小(不能少於1)和比較器的優先隊列 queue = new PriorityQueue<String>(30, new Comparator<String>(){ @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } });
PriorityQueue有很多常用方法,add、offer、poll、peek、element、remove、clear、size、isEmpty等,關於其他方法可以查看API。
常用方法說明如下:

//往優先隊列中插入元素,插入元素失敗時會拋出異常 boolean add(E e); //往優先隊列中插入元素,插入元素失敗時會返回false boolean offer(E e); //獲取並刪除隊列的第一個元素或隊列頭部的元素,刪除元素失敗時會返回null E poll(); //獲取隊列第一個元素或隊列頭部的元素,不刪除隊列中的元素,獲取不到返回null E peek(); //獲取隊列第一個元素或隊列頭部的元素,不刪除隊列中的元素,獲取不到會拋出異常 E element(); //從隊列中刪除元素的單個實例 E remove(); //刪除優先級隊列的所有內容 void clear(); //返回隊列中存在的元素數 int size(); //判斷改隊列是否為空 boolean isEmpty();
3.ConcurrentLinkedQueue
ConcurrentLinkedQueue是基於鏈接節點的無界線程安全隊列。此隊列按照FIFO(先進先出)原則對元素進行排序。隊列的頭部是隊列中時間最長的元素,隊列的尾部 是隊列中時間最短的元素。新的元素插入到隊列的尾部,隊列獲取操作從隊列頭部獲得元素。當多個線程共享訪問一個公共collection時,ConcurrentLinkedQueue是一個恰當的選擇,此隊列不允許使用null元素。
定義一個ConcurrentLinkedQueue的方式有如下幾種:

//創建一個ConcurrentLinkedQueue隊列 ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>(); //將其他類型的集合轉為ConcurrentLinkedQueue隊列 queue = new ConcurrentLinkedQueue<String>(new ArrayList<String>());
ConcurrentLinkedQueue有很多常用方法,add、offer、poll、peek、remove、clear、size、isEmpty等,關於其他方法可以查看API。
4.ArrayBlockingQueue
ArrayBlockingQueue是一個阻塞式的隊列,繼承自AbstractBlockingQueue,間接的實現了Queue接口和Collection接口。底層以數組的形式保存數據(實際上可看作一個循環數組)。
ArrayBlockingQueue通過使用全局獨占鎖實現同時只能有一個線程進行入隊或者出隊操作,有點類似在方法上添加synchronized。其中offer、poll操作通過簡單的加鎖進行入隊出隊操作,而put、take則使用了條件變量實現如果隊列滿則等待,如果隊列空則等待,然后分別在出隊和入隊操作中發送信號激活等待線程實現同步。另外相比LinkedBlockingQueue,ArrayBlockingQueue的size操作的結果是精確的,因為計算前加了全局鎖。
定義一個ArrayBlockingQueue的方式有如下幾種:

//創建一個ArrayBlockingQueue隊列,設置初始容量 ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(2); //創建一個ArrayBlockingQueue隊列,設置初始容量和是否為公平鎖 queue = new ArrayBlockingQueue<String>(2, false); //設置初始容量和是否為公平鎖並且將其他類型的集合轉為ArrayBlockingQueue隊列 queue = new ArrayBlockingQueue<String>(2, false, new ArrayList<String>());
ArrayBlockingQueue有很多常用方法,add、offer、put、poll、take、element、peek、remove、clear、size、isEmpty等,關於其他方法可以查看API。
常用方法說明如下:

//將指定的元素插入到此隊列的尾部,里面調用了offer方法,如果隊列滿了則拋出異常 boolean add(E e); //將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回 true,如果此隊列已滿,則返回 false boolean offer(E e); //將指定的元素插入此隊列的尾部,如果該隊列已滿則產生阻塞等待,直至可以添加元素為止 void put(E e); //獲取並移除此隊列的頭,如果此隊列為空,則返回 null E poll(); //獲取並移除此隊列的頭部,如果沒有元素則等待,直至獲取元素為止 E take(); //獲取但不移除此隊列的頭,如果此隊列為空,則返回 null E peek(); //從此隊列中移除指定元素的單個實例 E remove();
5.LinkedBlockingQueue
LinkedBlockingQueue是一個基於已鏈接節點的、范圍任意的blocking queue。此隊列按FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部是在隊列中時間最短的元素。新元素插入到隊列的尾部,並且隊列獲取操作會獲得位於隊列頭部的元素。鏈接隊列的吞吐量通常要高於基於數組的隊列,但是在大多數並發應用程序中,其可預知的性能要低。
LinkedBlockingQueue中也有兩個Node分別用來存放首尾節點,並且里面有個初始值為0的原子變量count用來記錄隊列元素個數,另外里面有兩個ReentrantLock的獨占鎖,分別用來控制元素入隊和出隊加鎖,其中takeLock用來控制同時只有一個線程可以從隊列獲取元素,其他線程必須等待,putLock控制同時只能有一個線程可以獲取鎖去添加元素,其他線程必須等待。另外notEmpty和notFull用來實現入隊和出隊的同步。 另外由於出入隊是兩個非公平獨占鎖,所以可以同時又一個線程入隊和一個線程出隊,其實這個是個生產者-消費者模型。
定義一個LinkedBlockingQueue的方式有如下幾種:

//創建一個LinkedBlockingQueue隊列,初始容量為Integer.MAX_VALUE LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>(); //創建一個LinkedBlockingQueue隊列,設置初始容量 queue = new LinkedBlockingQueue<String>(30); //設置初始容量為Integer.MAX_VALUE並且將其他類型的集合轉為LinkedBlockingQueue隊列 queue = new LinkedBlockingQueue<String>(new ArrayList<String>());
LinkedBlockingQueue有很多常用方法,add、offer、put、poll、take、element、peek、remove、clear、size、isEmpty等,關於其他方法可以查看API。
常用方法說明如下:

//將對象塞入隊列,如果塞入成功返回true, 否則返回異常 boolean add(E e); //將對象塞入到隊列中,如果設置成功返回true, 否則返回false boolean offer(E e); //將元素塞入到隊列中,如果隊列中已經滿了,則該方法會一直阻塞,直到隊列中有多余的空間 void put(E e); //從隊列中取對象,如果隊列中沒有對象,線程會一直阻塞,直到隊列中有對象,並且該方法取得了該對象 E take(); //在給定的時間里,從隊列中獲取對象,時間到了直接調用普通的poll方法,為null則直接返回null E poll(long timeout, TimeUnit unit); //獲取隊列中剩余長度 int remainingCapacity(); //從隊列中移除指定的值 boolean remove(Object o); //判斷隊列中是否包含該對象 public boolean contains(Object o); //將隊列中對象,全部移除,並加到傳入集合中 int drainTo(Collection<? super E> c);
6.PriorityBlockingQueue
PriorityBlockingQueue是一個支持優先級的無界阻塞隊列,雖然此隊列邏輯上是無界的,但是資源被耗盡時試圖執行 add 操作也將失敗(導致OutOfMemoryError錯誤)。默認情況下元素采用自然順序升序排列。也可以自定義類實現compareTo()方法來指定元素排序規則,或者初始化PriorityBlockingQueue時,指定構造參數Comparator來對元素進行排序。但需要注意的是不能保證同優先級元素的順序。PriorityBlockingQueue也是基於最小二叉堆實現,使用基於CAS實現的自旋鎖來控制隊列的動態擴容,保證了擴容操作不會阻塞take操作的執行。
由於這是一個優先級隊列所以有個比較器comparator用來比較元素大小。lock獨占鎖對象用來控制同時只能有一個線程可以進行入隊出隊操作。notEmpty條件變量用來實現take方法阻塞模式。這里沒有notFull條件變量是因為這里的put操作是非阻塞的,為啥要設計為非阻塞的是因為這是無界隊列。
定義一個PriorityBlockingQueue的方式有如下幾種:

//創建一個PriorityBlockingQueue隊列,初始容量為11 PriorityBlockingQueue<String> queue = new PriorityBlockingQueue<String>(); //創建一個PriorityBlockingQueue隊列,設置初始容量 queue = new PriorityBlockingQueue<String>(12); //將其他類型的集合轉為PriorityBlockingQueue隊列 queue = new PriorityBlockingQueue<String>(new ArrayList<String>()); //創建一個PriorityBlockingQueue隊列,初始化指定大小和比較器的優先隊列 queue = new PriorityBlockingQueue<String>(12, new Comparator<String>(){ @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } });
PriorityBlockingQueue有很多常用方法,add、offer、put、poll、take、element、peek、remove、clear、size、isEmpty等,關於其他方法可以查看API。
常用方法說明如下:

//向此隊列添加指定的元素 boolean add(E o); //將指定的元素插入到優先級隊列中 boolean offer(E o); //將指定的元素添加到優先級隊列中 void put(E o); //檢索,但是不移除此隊列的頭,如果此隊列為空,則返回 null E peek(); //檢索並移除此隊列的頭部,如果此隊列中沒有任何元素,則等待指定等待的時間(如果有必要) E poll(); //檢索並移除此隊列的頭部,如果此隊列不存在任何元素,則一直等待 E take(); //判斷隊列中是否包含該對象 boolean contains(Object o); //移除此隊列中所有可用的元素,並將它們添加到給定 collection中 int drainTo(Collection<? super E> c);
7.DelayQueue
DelayQueue是Delayed元素的一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部是延遲期滿后保存時間最長的Delayed元素。如果延遲都還沒有期滿,則隊列沒有頭部,並且poll將返回null。
DelayQueue中內部使用的是PriorityQueue存放數據,使用ReentrantLock實現線程同步,可知是阻塞隊列。另外隊列里面的元素要實現Delayed接口,一個是獲取當前剩余時間的接口,一個是元素比較的接口,因為這個是有優先級的隊列。
定義一個DelayQueue的方式有如下幾種:

public DelayQueue() {} public DelayQueue(Collection<? extends E> c) { this.addAll(c); }
DelayQueue有很多常用方法,add、offer、put、poll、take、element、peek、remove、clear、size、isEmpty等,關於其他方法可以查看API。
常用方法說明如下:

//將指定的元素插入此延遲隊列 boolean add(E e); //將指定的元素插入此延遲隊列 boolean offer(E e); //將指定的元素插入此延遲隊列 void put(E e); //檢索但不刪除此隊列的頭,如果此隊列為空,則返回null E peek(); //檢索並刪除此隊列的頭, 如果此隊列沒有延遲過期的元素,則返回null E poll(); //檢索並刪除此隊列的頭,如有必要,請等待直到延遲過期的元素在此隊列上可用 E take(); //從指定隊列中刪除指定元素的單個實例(如果存在),無論它是否已過期 boolean remove(Object o); //從此隊列中刪除所有可用的元素,並將它們添加到給定的集合中 int drainTo(Collection<? super E> c);
8.SynchronousQueue
SynchronousQueue是一個不存儲元素、沒有內部容量的阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行peek,因為僅在試圖要移除元素時,該元素才存在.除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)插入元素。也不能迭代隊列,因為其中沒有元素可用於迭代。隊列的頭是嘗試添加到隊列中的首個已排隊插入線程的元素。如果沒有這樣的已排隊線程,則沒有可用於移除的元素並且 poll()將會返回null。對於其他collection方法(例如contains),SynchronousQueue作為一個空collection。
對於正在等待的生產者和使用者線程而言,此類支持可選的公平排序策略,默認情況下不保證這種排序。但是,使用公平設置為true所構造的隊列可保證線程以FIFO的順序進行訪問。
定義一個SynchronousQueue的方式有如下幾種:

//創建一個SynchronousQueue隊列,不保證順序 SynchronousQueue<String> queue = new SynchronousQueue<String>(); //創建一個SynchronousQueue隊列,保證順序 queue = new SynchronousQueue<String>(true);
SynchronousQueue有很多常用方法,add、offer、put、poll、take、drainTo等,關於其他方法可以查看API。
常用方法說明如下:

//如果另一個線程正在等待接收指定的元素,則將其插入此隊列 boolean offer(E e); //將指定的元素添加到此隊列,如果有必要,等待另一個線程接收它 void put(E o); //如果另一個線程當前正在使元素可用,則檢索並刪除此隊列的頭部 E poll(); //始終返回null E peek(); //檢索並刪除此隊列的頭,如有必要,等待其他線程將其插入 E take(); //從此隊列中刪除所有可用的元素,並將它們添加到給定的集合中 int drainTo(Collection<? super E> c); //始終返回false boolean contains(Object o);
9.LinkedBlockingDeque
LinkedBlockingDeque是雙向鏈表實現的雙向並發阻塞隊列。該阻塞隊列同時支持FIFO和FILO兩種操作方式,即可以從隊列的頭和尾同時操作(插入/刪除);並且,該阻塞隊列是支持線程安全。LinkedBlockingDeque還是可選容量的(防止過度膨脹),即可以指定隊列的容量。如果不指定,默認容量大小等於Integer.MAX_VALUE。
定義一個LinkedBlockingDeque的方式有如下幾種:

//創建一個LinkedBlockingDeque隊列,初始容量為Integer.MAX_VALUE LinkedBlockingDeque<String> queue = new LinkedBlockingDeque<String>(); //創建一個LinkedBlockingDeque隊列,設置初始容量 queue = new LinkedBlockingDeque<String>(); //設置初始容量為Integer.MAX_VALUE並且將其他類型的集合轉為LinkedBlockingDeque隊列 queue = new LinkedBlockingDeque<String>(new ArrayList<String>());
LinkedBlockingDeque有很多常用方法,add、addFirst、、offer、put、poll、take、drainTo等,關於其他方法可以查看API。
常用方法說明如下:

//在此雙端隊列的末尾插入指定的元素,除非會違反容量限制 boolean add(E e); //如果可以在不違反容量限制的情況下立即執行此操作,則將指定的元素插入此雙端隊列的前面,如果當前沒有可用空間,則拋出IllegalStateException void addFirst(E e); //如果可以立即執行此操作,而不會違反容量限制,則在此雙端隊列的末尾插入指定的元素,如果當前沒有可用空間,則拋出IllegalStateException void addLast(E e); //如果可以在不違反容量限制的情況下立即執行操作,則將指定的元素插入此雙端隊列表示的隊列中(換句話說,在此雙端隊列的末尾),如果成功則返回true,如果當前沒有可用空間,則返回 false boolean offer(E e); //如果可以立即執行此操作,而不會違反容量限制,則在此雙端隊列的前面插入指定的元素;如果成功,則返回true;如果當前沒有可用空間,則返回false boolean offerFirst(E e); //將指定的元素插入此雙端隊列的前面,如果空間足夠,則需要等待指定的等待時間 boolean offerFirst(E e, long timeout, TimeUnit unit); //如果可以立即執行此操作,而不會違反容量限制,則在此雙端隊列的末尾插入指定的元素;如果成功,則返回true;如果當前沒有可用空間,則返回false boolean offerLast(E e); //將指定的元素插入此雙端隊列表示的隊列中(換句話說,在此雙端隊列的末尾),如有必要,請等待空間變為可用 void put(E e); //將指定的元素插入此雙端隊列的前面,如有必要,請等待空間變大 void putFirst(E e); //將指定的元素插入此雙端隊列的末尾,如有必要,請等待空間變大 void putLast(E e); //檢索並刪除此雙端隊列表示的隊列的頭部(換句話說,此雙端隊列的第一個元素),如果此雙端隊列為空,則返回 null E poll(); //檢索並刪除此雙端隊列代表的隊列的頭部(換句話說,此雙端隊列的第一個元素),如果有必要使元素變為可用,則等待指定的等待時間 E poll(long timeout, TimeUnit unit); //檢索並刪除此雙端隊列的第一個元素,如果此雙端隊列為空,則返回null E pollFirst(); //檢索並刪除此雙端隊列的第一個元素,並在必要時等待指定的等待時間,以使元素變為可用 E pollFirst(long timeout, TimeUnit unit); //檢索並刪除此雙端隊列的最后一個元素,如果此雙端隊列為空,則返回null E pollLast(); //檢索並刪除此雙端隊列的最后一個元素,並在必要時等待指定的等待時間,以使元素變為可用 E pollLast(long timeout, TimeUnit unit); //檢索並刪除此雙端隊列代表的隊列的頭部(換句話說,此雙端隊列的第一個元素),如有必要,請等待直到某個元素變為可用 E take(); //檢索並刪除此雙端隊列的第一個元素,如有必要,請等待直到元素可用 E takeFirst(); //檢索並刪除此雙端隊列的最后一個元素,如有必要,請等待直到元素可用 E takeLast(); //檢索但不刪除此雙端隊列代表的隊列的頭(換句話說,此雙端隊列的第一個元素),如果此雙端隊列為空,則返回null E peek(); //檢索但不刪除此雙端隊列的第一個元素,如果此雙端隊列為空,則返回null E peekFirst(); //檢索但不刪除此雙端隊列的最后一個元素,如果此雙端隊列為空,則返回null E peekLast(); //檢索但不刪除此雙端隊列代表的隊列的頭 E element(); //檢索但不刪除此雙端隊列的第一個元素 E getFirst(); //檢索但不刪除此雙端隊列的最后一個元素 E getLast(); //檢索並刪除此雙端隊列代表的隊列的頭部 E remove(); //從此雙端隊列刪除指定元素的第一次出現 boolean remove(Object o); //檢索並刪除此雙端隊列的第一個元素 E removeFirst(); //從此雙端隊列刪除指定元素的第一次出現 boolean removeFirstOccurrence(Object o); //檢索並刪除此雙端隊列的最后一個元素 E removeLast(); //從此雙端隊列移除最后一次出現的指定元素 boolean removeLastOccurrence(Object o); //從原子上刪除此雙端隊列中的所有元素 void clear(); //從此隊列中刪除所有可用的元素,並將它們添加到給定的集合中 int drainTo(Collection<? super E> c); //從此隊列中最多移除給定數量的可用元素,並將它們添加到給定的集合中 int drainTo(Collection<? super E> c, int maxElements); //從此雙端隊列表示的堆棧中彈出一個元素 E pop(); //將一個元素壓入此雙端隊列表示的堆棧上 void push(E e); //此雙端隊列是否包含指定的元素 boolean contains(Object o); //返回此雙端隊列理想情況下(在沒有內存或資源約束的情況下)可以接受而不會阻塞的其他元素的數量 int remainingCapacity(); //返回此雙端隊列的元素數量 int size();