阻塞佇列 DelayQueue 原始碼解析
DelayQueue
- 一個支援延時獲取元素的無界阻塞佇列,裡面的元素全部都是“可延期”的元素,**列頭的元素必須是最先“到期”**的元素,如果列頭不出隊,其他元素是無法出隊的。
- 主要用於兩個方面:快取(清掉快取中超時的快取資料)、任務超時處理
- 底層實現是優先佇列,在出隊時呼叫了內部優先佇列 peek 方法先查詢其隊首元素,如果到期了才呼叫優先佇列的 poll 讓隊首出隊,否則就阻塞等隊首到期,也就是說優先佇列中只有前一個元素到期並出隊後,後面的元素才能出隊。併發安全也是通過 Lock。
元素
- 元素必須實現 Delayed 介面
// 實現該介面的getDelay()方法,同時定義compareTo()方法即可。
// 注意:ompareTo 方法提供的排序,必須與過期時間一致,不然就會出現最小堆堆頂並不是最先過期的元素
public interface Delayed extends Comparable<Delayed> {
// 返回與此物件相關的的剩餘時間
long getDelay(TimeUnit unit);
}
屬性
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 會超時等待,通常為第一個查詢到未到期頭節點的執行緒 */
private Thread leader = null;
/** 等待有可出隊元素 */
private final Condition available = lock.newCondition();
/**
* 省略很多程式碼
*/
}
入隊
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
// 獲取鎖
lock.lock();
try {
// 向 PriorityQueue中插入元素
q.offer(e);
// 如果當前元素的對首元素(優先順序最高),leader設定為空,喚醒所有等待執行緒
// peek 是查詢而非出隊
if (q.peek() == e) {
// 設定為 null 防止記憶體洩漏
leader = null;
available.signal();
}
// 無界佇列,永遠返回true
return true;
} finally {
lock.unlock();
}
}
出隊
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 獲取鎖
lock.lockInterruptibly();
try {
for (;;) {
// 查詢隊首元素
E first = q.peek();
// 隊首為空,阻塞,等待off()操作喚醒
if (first == null)
available.await();
else {
// 獲取對首元素的超時時間
long delay = first.getDelay(NANOSECONDS);
// <=0 表示已過期,出對,return
if (delay <= 0)
// 只有這一個出口,只有隊首元素到期隊才可以出隊
return q.poll();
// 不設為 null 會引起記憶體洩漏
// 因為可能有多個執行緒來獲取隊首,結果沒到期全都阻塞,那麼這麼多個執行緒都持有了隊首元素的引用
// 但是隊首到期成功出隊後,如果其他那麼多個執行緒還在阻塞,那這個隊首使用完就無法被回收
first = null; // don't retain ref while waiting
// leader != null 證明有比它來的早的執行緒已經在超時等待隊首到期了
if (leader != null)
// 所以不設定等待超時時間,因為隊首到期出對後,那個執行緒會喚醒下一個等待的執行緒
available.await();
else {
// leader 為空說明是隊首的元素未到期,就將leader 設定為當前執行緒
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 超時阻塞,時間為隊首到期時間
available.awaitNanos(delay);
} finally {
// 釋放leader,後面的元素就可以使用超時等待了
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 喚醒阻塞執行緒(一般喚醒的是隊首後面阻塞等待出隊的元素)
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
相關文章
- Java併發包原始碼學習系列:阻塞佇列實現之DelayQueue原始碼解析Java原始碼佇列
- 延遲阻塞佇列 DelayQueue佇列
- 阻塞佇列BlockingQueue(三)--DelayQueue佇列BloC
- 阻塞佇列 SynchronousQueue 原始碼解析佇列原始碼
- 阻塞佇列 LinkedTransferQueue 原始碼解析佇列原始碼
- 阻塞佇列 PriorityBlockingQueue 原始碼解析佇列BloC原始碼
- Java中常用的七個阻塞佇列第二篇DelayQueue原始碼介紹Java佇列原始碼
- Laravel 佇列原始碼解析Laravel佇列原始碼
- Java併發包原始碼學習系列:阻塞佇列實現之SynchronousQueue原始碼解析Java原始碼佇列
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedTransferQueue原始碼解析Java原始碼佇列
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingDeque原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之PriorityBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之ArrayBlockingQueue原始碼解析Java原始碼佇列BloC
- 佇列、阻塞佇列佇列
- 阻塞佇列一——java中的阻塞佇列佇列Java
- 探討阻塞佇列和執行緒池原始碼佇列執行緒原始碼
- 原始碼剖析ThreadPoolExecutor執行緒池及阻塞佇列原始碼thread執行緒佇列
- Java併發包原始碼學習系列:基於CAS非阻塞併發佇列ConcurrentLinkedQueue原始碼解析Java原始碼佇列
- 從原始碼解析-Android資料結構之單向阻塞佇列LinkedBlockingQueue的使用原始碼Android資料結構佇列BloC
- 迴歸Java基礎:LinkedBlockingQueue阻塞佇列解析JavaBloC佇列
- 阻塞佇列--LinkedBlockingQueue佇列BloC
- 阻塞佇列 BlockingQueue佇列BloC
- 死磕阻塞佇列佇列
- Java中的阻塞佇列Java佇列
- 阻塞佇列——四組API佇列API
- 多執行緒程式設計-分析阻塞佇列的原始碼實現執行緒程式設計佇列原始碼
- [原始碼解析] 訊息佇列 Kombu 之 基本架構原始碼佇列架構
- DelayQueue系列(一):原始碼分析原始碼
- 原始碼解析Synchronous Queue 這種特立獨行的佇列原始碼佇列
- AQS原始碼深入分析之條件佇列-你知道Java中的阻塞佇列是如何實現的嗎?AQS原始碼佇列Java
- 聊聊併發(四)——阻塞佇列佇列
- Java併發包原始碼學習系列:阻塞佇列BlockingQueue及實現原理分析Java原始碼佇列BloC
- laravel原始碼分析-佇列QueueLaravel原始碼佇列
- [原始碼解析] 分散式任務佇列 Celery 之啟動 Consumer原始碼分散式佇列
- 處理線上RabbitMQ佇列阻塞MQ佇列
- 阻塞佇列 BlockingQueue(二)--ArrayBlockingQueue與LinkedBlockingQueue佇列BloC
- Java併發系列 — 阻塞佇列(BlockingQueue)Java佇列BloC