您好,登錄后才能下訂單哦!
1、DelayQueue是×××阻塞隊列
2、隊列中的元素必須實現Delayed接口,只有當該對象的getDalay方法返回的剩余時間≤0時才會出隊。
3、剩余時間最小的元素就在堆頂,每次出隊其實就是刪除剩余時間≤0的最小元素。
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
/**
* leader線程是首個嘗試出隊元素(隊列不為空)但被阻塞的線程.
* 該線程會限時等待(隊首元素的剩余有效時間),用于喚醒其它等待線程
* 這個很關鍵,當隊列為空或者堆頂元素還沒達到出隊時間時,所有線程都會阻塞的,要保證有線程可以喚醒,而leader線程就是這個首先自己喚醒自己的線程,然后才能繼續喚醒其他線程。
*/
private Thread leader = null;
/**
* 出隊線程條件隊列, 當有多個線程, 會在此條件隊列上等待.
*/
private final Condition available = lock.newCondition();
}
為了提升性能,DelayQueue并不會讓所有出隊線程都無限等待,而是用leader保存了第一個嘗試出隊的線程,該線程的等待時間是隊首元素的剩余有效期。這樣,一旦leader線程自己喚醒(此時隊首元素也失效了),就可以出隊成功,然后喚醒一個其它在available條件隊列上等待的線程。之后,會重復上一步,新喚醒的線程可能取代成為新的leader線程。
/**
* 入隊一個指定元素e.
* 由于是×××隊列, 所以該方法并不會阻塞線程.
*/
public void put(E e) {
offer(e);
}
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e); // 調用PriorityQueue的offer方法
// 如果入隊元素在隊首, 則喚醒一個出隊線程
// 當首次入隊元素時,需要喚醒一個出隊線程
// 因為此時可能已有出隊線程在空隊列上等待了
//如果不喚醒,會導致出隊線程永遠無法執行。
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
1.隊列為空,直接阻塞出隊線程,在available條件隊列等待
2.隊列非空,還要看隊首元素的有效期,如果隊首元素過期了,那直接出隊就行了;如果隊首元素未過期,就要看leader是否為空,如果不是,就無限等待,如果是,則自己成為leader,限時等待。
/**
* 隊首出隊元素.
* 如果隊首元素(堆頂)未到期或隊列為空, 則阻塞線程.
*/
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (; ; ) { //出隊是一個自旋操作
E first = q.peek(); // 讀取隊首元素
if (first == null) // CASE1: 隊列為空, 直接阻塞
available.await();
else { // CASE2: 隊列非空
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0) // CASE2.0: 隊首元素已過期,直接出隊
return q.poll();
// 執行到此處說明隊列非空, 且隊首元素未過期
first = null;
if (leader != null) // CASE2.1: 已存在leader線程
available.await(); // 無限期阻塞當前線程
else { // CASE2.2: 不存在leader線程
Thread thisThread = Thread.currentThread();
leader = thisThread; // 將當前線程置為leader線程
try {
available.awaitNanos(delay); // 阻塞當前線程(限時等待剩余有效時間)
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null) // 不存在leader線程,而且隊列不空, 則喚醒一個其它出隊線程,防止任務無法執行。
available.signal();
lock.unlock();
}
}
1、DelayQueue是阻塞隊列中非常有用的一種隊列,經常被用于緩存或定時任務等的設計。
2、ScheduledThreadPoolExecutor.DelayedWorkQueue就是一種延時阻塞隊列。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。