目錄
LinkedBlockingQueue表示一個基於鏈表實現的可選的固定容量的,先進先出的,線程安全的隊列(棧),其接口的語義和ArrayBlockingQueue基本一致;DelayQueue 表示一個基於PriorityQueue優先級隊列實現的無固定容量的,先進先出的,線程安全的隊列,跟ArrayBlockingQueue相比,獲取元素時增加了有效期判斷,如果已過期則返回,否則返回null或者阻塞等待其過期。本篇博客就來詳細探討這兩個類的實現細節。
一、LinkedBlockingQueue
1、定義
該類的類繼承關係跟ArrayBlockingQueue是一樣,兩者的差異主要是實現機制不同,如下:
LinkedBlockingQueue是基於鏈表的,內部類Node用來描述鏈表中的一個節點,其定義如下:
該類包含的屬性如下:
/** 隊列的容量 */
private final int capacity;
/**隊列中元素的個數 */
private final AtomicInteger count = new AtomicInteger();
/**鏈表頭*/
transient Node<E> head;
/**鏈表尾*/
private transient Node<E> last;
/** 取出元素時獲取的鎖 */
private final ReentrantLock takeLock = new ReentrantLock();
/** 隊列是空時的等待條件 */
private final Condition notEmpty = takeLock.newCondition();
/** 插入元素時獲取的鎖 */
private final ReentrantLock putLock = new ReentrantLock();
/** 隊列是滿的等待條件*/
private final Condition notFull = putLock.newCondition();
因爲跟ArrayBlockingQueue實現的接口是一樣的,所以各方法的用途和接口邏輯是基本一樣的,下面重點分析兩者間的實現上的差異。
2、構造方法
//不限定容量的,默認容量就是int的最大值
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
//限定容量
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
//初始化時head節點
last = head = new Node<E>(null);
}
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); //加鎖的目的主要是爲了保證修改對其他CPU立即可見
try {
int n = 0;
//遍歷集合c,將裏面的元素加入到隊列中
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
//加入到隊列中
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
3、put / offer
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
//此處使用AtomicInteger,避免因爲獲取鎖導致等待
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
//創建一個新節點
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
//加入到隊列中
enqueue(node);
//增加元素個數
c = count.getAndIncrement();
if (c + 1 < capacity)
//隊列中還有可用容量,喚醒因爲隊列滿了而阻塞的線程
//ArrayBlockingQueue中插入元素和獲取元素是同一個鎖,從隊列中取出一個元素時會喚醒因爲隊列滿而等待的線程
//LinkedBlockingQueue是兩個鎖,從隊列中取出元素
notFull.signal();
}
} finally {
putLock.unlock();
}
//c是插入之前的元素個數,只有個數是0的情況下才可能存在因爲隊列是空的而阻塞的線程
if (c == 0)
//喚醒因爲隊列是空的而等待的線程
signalNotEmpty();
return c >= 0;
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
//如果被中斷了拋出異常
putLock.lockInterruptibly();
try {
//隊列滿了,循環等待,直到列隊有可用容量或者等待超時了
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
//隊列中還有可用容量,喚醒因爲隊列滿了而阻塞的線程
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
//隊列滿了,循環等待,直到列隊有可用容量
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
//插入到鏈表的後面
private void enqueue(Node<E> node) {
last = last.next = node;
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
//喚醒因爲隊列是空的而等待的線程
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
4、peek / poll /take
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//隊列爲空則循環等待
while (count.get() == 0) {
notEmpty.await();
}
//獲取並移除鏈表頭
x = dequeue();
//元素個數減1,返回減1前的值
c = count.getAndDecrement();
if (c > 1)
//喚醒因爲隊列是空的而阻塞的線程,c大於1,這次take移除了1個,還剩至少1個
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
//喚醒因爲隊列是滿的而等待的線程
signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//隊列爲空則循環等待,如果超時則返回null
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll() {
final AtomicInteger count = this.count;
//爲空直接返回null
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
//爲空直接返回null,不用等待
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
private E dequeue() {
//獲取並移除鏈表頭
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
//喚醒因爲隊列是滿的而等待的線程
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
5、remove / clear / drainTo
public boolean remove(Object o) {
if (o == null) return false;
//需要同時獲取兩個鎖,因爲移除的時候可能是移除鏈表尾部的節點也可能是鏈表中間的節點
//前者需要獲取takeLock,後者必須獲取putLock,因爲需要修改鏈表關係
fullyLock();
try {
//從head往後遍歷,p表示當前節點,trail表示該節點的前一個節點
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
//如果找到目標節點,將其從鏈表中移除
unlink(p, trail);
return true;
}
}
return false;
} finally {
//釋放兩個鎖
fullyUnlock();
}
}
public void clear() {
fullyLock();
try {
//遍歷整個鏈表,將各節點的鏈表關係和item屬性都去除
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
if (count.getAndSet(0) == capacity)
//如果修改前容量是滿的,則喚醒因爲隊列是滿的而阻塞的線程
notFull.signal();
} finally {
fullyUnlock();
}
}
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
public int drainTo(Collection<? super E> c, int maxElements) {
//校驗參數合法性
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
boolean signalNotFull = false;
//因爲是取的鏈表頭,所以需要獲取兩個鎖,只需要一個takeLock即可
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
//取maxElements和元素個數的最小值
int n = Math.min(maxElements, count.get());
// count.get provides visibility to first n Nodes
Node<E> h = head;
int i = 0;
try {
//從head開始往後遍歷,取n個元素,head是最早插入的
while (i < n) {
Node<E> p = h.next;
//將鏈表節點保存的元素引用放入集合中
c.add(p.item);
//鏈表引用關係清除
p.item = null;
h.next = h;
h = p;
++i;
}
return n;
} finally {
//n大於0,此處i肯定大於0,因爲獲取鎖takeLock後,就只有當前線程可以移除節點
if (i > 0) {
//重置head
head = h;
//如果取出元素前隊列是滿的,則喚醒因爲隊列滿的而等待的線程
signalNotFull = (count.getAndAdd(-i) == capacity);
}
}
} finally {
takeLock.unlock();
if (signalNotFull)
signalNotFull();
}
}
void fullyLock() {
putLock.lock();
takeLock.lock();
}
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
void unlink(Node<E> p, Node<E> trail) {
//將p從鏈表中移除
p.item = null;
trail.next = p.next;
if (last == p)
//如果p是最後一個節點,將trail作爲last
last = trail;
if (count.getAndDecrement() == capacity)
//如果移除前鏈表是滿的,則喚醒因爲隊列滿了而等待的線程
notFull.signal();
}
6、iterator / Itr
iterator返回一個遍歷器實例,如下:
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
//next方法返回的節點
private Node<E> current;
//上一次next方法返回的節點
private Node<E> lastRet;
//next方法返回的元素
private E currentElement;
Itr() {
fullyLock();
try {
//因爲head節點初始化時是一個空的Node節點,所以遍歷的第一個節點是head的next節點
current = head.next;
if (current != null)
currentElement = current.item;
} finally {
fullyUnlock();
}
}
public boolean hasNext() {
return current != null;
}
private Node<E> nextNode(Node<E> p) {
for (;;) {
Node<E> s = p.next;
if (s == p)
//s等於p說明這是一個無效節點,返回鏈表頭的有效節點
return head.next;
//s等於null,p是最後一個節點
if (s == null || s.item != null)
return s;
//如果s不等於null但是item爲null,則進入下一個節點
//此時通常s等於p了
p = s;
}
}
public E next() {
//獲取兩個鎖
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
E x = currentElement;
//保存上一次的
lastRet = current;
//獲取下一個節點
current = nextNode(current);
//獲取下一個返回的元素
currentElement = (current == null) ? null : current.item;
return x;
} finally {
fullyUnlock();
}
}
public void remove() {
if (lastRet == null)
throw new IllegalStateException();
fullyLock();
try {
Node<E> node = lastRet;
lastRet = null;
//從head開始往後遍歷
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (p == node) {
//找到目標節點並移除
unlink(p, trail);
break;
}
}
} finally {
fullyUnlock();
}
}
}
7、跟ArrayBlockingQueue的實現差異總結
主要有以下幾點:
- ArrayBlockingQueue是基於循環數組實現的,容量是固定的,循環數組是指插入或者移除元素到達數組末尾時又會從頭開始;LinkedBlockingQueue 是基於單向鏈表的,如果指定容量就是固定容量,如果沒有指定,則容量默認是int的最大值。
- ArrayBlockingQueue中維護元素的個數的屬性是一個普通的int類型,讀取和修改該屬性必須獲取鎖;LinkedBlockingQueue中是一個AtomicInteger類型,這樣在判斷隊列是否爲空時就不需要加鎖了,可以快速返回,避免因爲獲取鎖而等待。
- ArrayBlockingQueue中插入元素和移除元素時都需要獲取同一個鎖,即這兩個動作不能同時進行,這是因爲移除元素時,移除元素的索引takeIndex不能超過插入元素的索引putIndex,如果等於就表示隊列中元素已經都空了;LinkedBlockingQueue中插入元素和從鏈表頭移除元素是兩把鎖,putLock和takeLock,這樣可以讓兩個操作同時進行效率,效率更高;之所以能使用兩把鎖,這是因爲往鏈表中插入元素是插入到鏈表尾,而移除元素是移除鏈表頭的元素,兩個是互相獨立的。但是執行clear和remove方法,執行Itr的next和remove方法時必須同時獲取兩把鎖,這是因爲這種場景下不允許隊列發生修改。
- ArrayBlockingQueue中通過內部類Itr執行遍歷時不需要上鎖,ArrayBlockingQueue執行特定操作時會通過負責跟蹤Itr的Itrs的回調方法,通過Itr比如某個對象被刪除了,然後Itr會重新調整自己下一個遍歷的元素,整體邏輯比較複雜;LinkedBlockingQueue的實現比較簡單,執行next和remove方法時要獲取兩個鎖,就避免了再遍歷時鏈表發生改變了,實現相對簡單明瞭。
- ArrayBlockingQueue每插入一個元素後都需要調用signal方法喚醒因爲隊列是空的而阻塞的線程,即使此時沒有等待的線程;LinkedBlockingQueue會校驗插入元素前的隊列容量,如果插入前的隊列容量是0,那麼有可能有線程因爲隊列是空的而阻塞,纔會調用signal方法,減少不必要的調用。
- ArrayBlockingQueue每移除一個元素後都需要調用signal方法喚醒因爲隊列是滿的而阻塞的線程,即使此時沒有等待的線程;LinkedBlockingQueue會校驗移除元素前隊列的容量,如果隊列是滿的,纔有可能存在因爲隊列是滿的而阻塞的線程,纔會調用signal方法喚醒阻塞的線程,減少不必要的調用。
- ArrayBlockingQueue執行clear或者drainTo方法時,移除了指定數量的元素,就會通過for循環調用該數量次的signal方法,喚醒因爲線程滿了而阻塞的線程,不能超過該數量且在Condition上有等待的線程。之所以不能超過該數量,是爲了避免喚醒過多的線程,他們會因爲隊列滿了而再次阻塞。LinkedBlockingQueue判斷移除前隊列的數量是滿的,則通過signal方法喚醒因爲隊列是滿的而阻塞的線程,但是隻調用一次,因爲隊列是滿的線程被喚醒後,會嘗試插入到隊列中,如果插入成功後還有多餘的容量,則會嘗試喚醒下一個因爲隊列是滿的而阻塞的線程,從而巧妙的避免了線程被喚醒後因爲隊列是滿的而再次阻塞的問題。
- LinkedBlockingQueue在移除元素時,如果移除前隊列容量大於1,會喚醒因爲隊列是空的而阻塞的線程,這是因爲LinkedBlockingQueue下元素插入時,只有隊列原來是空纔會喚醒因爲隊列是空的而阻塞的線程,即如果有多個阻塞的線程,這種情形下只會喚醒一個。這時已經被喚醒的線程會判斷隊列中是否有多餘的元素,如果有則繼續喚醒下一個阻塞的線程,如此將所有阻塞的線程都喚醒。ArrayBlockingQueue下因爲每次插入時都要喚醒一次因爲隊列是空而阻塞的線程,就不存在上述問題了。
二、DelayQueue
1、定義
DelayQueue的類繼承關係如下:
其中Delayed接口並不是DelayQueue直接實現的,而是加入到DelayQueue中的元素實現的接口,其定義如下:
其中Comparable接口主要用於排序使用,getDelay方法用於判斷這個元素是否過期,即是否滿足指定的延遲時間,返回0或者-1表示已過期,返回一個正值表示剩餘的有效期,入參unit就是爲了將剩餘的有效期轉換成unit指定的單位,DelayQueue使用的是納秒。
DelayQueue包含的屬性如下:
//同步的鎖
private final transient ReentrantLock lock = new ReentrantLock();
//實際保存隊列元素的優先級隊列
private final PriorityQueue<E> q = new PriorityQueue<E>();
//記錄第一個等待獲取已過期元素的線程,leader線程阻塞時可以根據鏈表頭元素的剩餘有效期設置等待超時時間,避免無期限等待,其他非leader線程在沒有設置等待超時的情形下因爲無法預知鏈表元素的有效期只能無期限等待,等待leader線程被喚醒獲取鏈表頭元素後,就喚醒下一個等待的線程,然後按照相同的方式同樣設置等待超時時間
private Thread leader = null;
//沒有已過期的元素,則在此Condition上等待
private final Condition available = lock.newCondition();
2、使用
DelayQueue主要適用於緩存系統,調度系統等對時間比較敏感的系統實現中,測試用例如下:
class CacheData<T> implements Delayed {
private T data;
private long deadline;
public CacheData(T data, int cacheTime) {
this.data = data;
this.deadline=System.currentTimeMillis()+cacheTime;
}
public T getData() {
return data;
}
@Override
public int compareTo(Delayed o) {
long result = this.getDelay(TimeUnit.NANOSECONDS)
- o.getDelay(TimeUnit.NANOSECONDS);
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
} else {
return 0;
}
}
@Override
public long getDelay(TimeUnit unit) {
//計算剩餘有效期
long diff = deadline-System.currentTimeMillis();
if(diff>0) {
//轉換成指定的單位
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
return diff;
}
}
public class DelayQueueTest {
@Test
public void test() throws Exception {
CacheData<String> a=new CacheData<>("a",1000);
CacheData<String> a2=new CacheData<>("b",2000);
CacheData<String> a3=new CacheData<>("c",3000);
CacheData<String> a4=new CacheData<>("d",4000);
CacheData<String> a5=new CacheData<>("e",5000);
DelayQueue<CacheData> delayQueue=new DelayQueue<>();
delayQueue.add(a);
delayQueue.add(a2);
delayQueue.add(a3);
delayQueue.add(a4);
delayQueue.add(a5);
CountDownLatch countDownLatch=new CountDownLatch(delayQueue.size());
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
long start=System.currentTimeMillis();
CacheData<String> cacheData=delayQueue.take();
System.out.println("get data->"+cacheData.getData()+",time->"+(System.currentTimeMillis()-start));
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for(int i=0;i<5;i++){
new Thread(runnable).start();
}
countDownLatch.await();
System.out.println("main end");
}
}
其運行結果如下:
//線程阻塞等待的時間跟這條記錄的有效期的時間基本一致
get data->a,time->996
get data->b,time->1995
get data->c,time->2995
get data->d,time->3994
get data->e,time->4993
main end
3、add / offer / put
這裏並不是嚴格按照BlockingQueue接口的語義來實現方法的,都是利用不帶時間參數的offer方法來實現的
public boolean add(E e) {
return offer(e);
}
public void put(E e) {
offer(e);
}
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//加入到優先級隊列中
q.offer(e);
if (q.peek() == e) {
//等於e,說明該隊列原來是空的
leader = null;
//喚醒等待的線程
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
4、poll / take / peek
這幾個方法的實現符合BlockingQueue接口的語義,跟ArrayBlockingQueue一致,區別在於返回元素前需要檢查元素是否過期了,如果未過期則返回null,否則會移除並返回隊列頭元素。
//peek返回隊列頭元素,不移除
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.peek();
} finally {
lock.unlock();
}
}
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//獲取隊列頭元素
E first = q.peek();
//如果爲空或者未過期則返回null
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
//不爲空且已過期,則返回並移除
return q.poll();
} finally {
lock.unlock();
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null) {
//如果隊列爲空則循環等待
if (nanos <= 0)
//等待超時返回null
return null;
else
nanos = available.awaitNanos(nanos);
} else {
//隊列不爲空,delay表示剩餘的有效期
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0) //已過期
return q.poll();
if (nanos <= 0) //未過期,等待超時
return null;
first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
//如果等待的時間小於有效期或者leader不爲空,則等待指定時間
nanos = available.awaitNanos(nanos);
else {
//如果等待時間大於有效期,則等待有效期
Thread thisThread = Thread.currentThread();
//記錄等待的線程
leader = thisThread;
try {
//等待指定時間,返回已經等待的時間
//調用awaitNanos後會釋放鎖,這時其他的調用poll方法並獲取鎖的線程就會發現leader非空了,然後按照最長的時間來等待
long timeLeft = available.awaitNanos(delay);
nanos -= delay - timeLeft;
} finally {
//被喚醒後將leader重置爲null
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//leader線程被喚醒後會將leader置爲null,如果還有節點,則喚醒下一個等待的線程
if (leader == null && q.peek() != null)
available.signal(); //喚醒等待的線程
lock.unlock();
}
}
//邏輯同上,就是無法指定等待的時間
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
//無期限等待,直到被leader線程喚醒
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//釋放鎖後,其他進入此方法的線程後,leader屬性就不爲空了
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//leader線程被喚醒後會將leader置爲null,如果還有節點,則喚醒下一個等待的線程
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
5、remove / clear /drainTo
remove和clear方法都是直接使用PriorityQueue的方法,drainTo方法從隊列中移除元素時,會判斷元素是否過期,如果已過期則添加到集合中,否則終止遍歷,返回已添加到集合中的元素個數
public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//移除某個元素
return q.remove(o);
} finally {
lock.unlock();
}
}
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//清空隊列
q.clear();
} finally {
lock.unlock();
}
}
//返回已添加到集合中的元素個數
public int drainTo(Collection<? super E> c) {
//校驗參數合法性
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = 0;
//將已過期的元素從隊列中移除加入到集合c中
for (E e; (e = peekExpired()) != null;) {
c.add(e); // In this order, in case add() throws.
q.poll();
++n;
}
return n;
} finally {
lock.unlock();
}
}
public int drainTo(Collection<? super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = 0;
for (E e; n < maxElements && (e = peekExpired()) != null;) {
c.add(e); // In this order, in case add() throws.
q.poll();
++n;
}
return n;
} finally {
lock.unlock();
}
}
private E peekExpired() {
//判斷鏈表頭元素是否已過期,如果未過期返回null
E first = q.peek();
return (first == null || first.getDelay(NANOSECONDS) > 0) ?
null : first;
}
6、iterator / Itr
元素的遍歷是基於當前隊列的快照,即遍歷過程中無法感知到隊列的修改;元素遍歷過程中被移除了,底層是通過PriorityQueue的迭代器來移除的。
public Iterator<E> iterator() {
//遍歷的是當前隊列的一個快照,即遍歷的過程中無法感知隊列的修改
return new Itr(toArray());
}
private class Itr implements Iterator<E> {
//保存隊列元素的數組
final Object[] array; // Array of all elements
//下一個返回的元素的索引
int cursor; // index of next element to return
//上一次返回的元素的索引
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
//cursor的初始值就是0
lastRet = -1;
this.array = array;
}
public boolean hasNext() {
return cursor < array.length;
}
@SuppressWarnings("unchecked")
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
//返回cursor當前值對應的數組元素,然後再將其加1
return (E)array[cursor++];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
//移除lastRet對應的元素
removeEQ(array[lastRet]);
lastRet = -1;
}
}
void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//遍歷PriorityQueue,將其從隊列中移除
for (Iterator<E> it = q.iterator(); it.hasNext(); ) {
if (o == it.next()) {
//找到目標 元素
it.remove();
break;
}
}
} finally {
lock.unlock();
}
}
public Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.toArray();
} finally {
lock.unlock();
}
}