DelayQueue基本簡(jiǎn)介
DelayQueue是一個(gè)無界阻塞隊(duì)列言疗,只有在延遲期滿時(shí)才能從中提取元素。該隊(duì)列的頭部是延遲期滿后保存時(shí)間最長(zhǎng)的Delayed 元素。
DelayQueue是一個(gè)用來延時(shí)處理的隊(duì)列速客,所謂延時(shí)處理就是說可以為隊(duì)列中元素設(shè)定一個(gè)過期時(shí)間渣触,相關(guān)的操作受到這個(gè)設(shè)定時(shí)間的控制诽俯。
DelayQueue使用場(chǎng)景
- 關(guān)閉空閑連接敲才。服務(wù)器中晦墙,有很多客戶端的連接,空閑一段時(shí)間之后需要關(guān)閉之歧斟。
- 緩存纯丸。緩存中的對(duì)象,超過了空閑時(shí)間静袖,需要從緩存中移出觉鼻。
- 任務(wù)超時(shí)處理。在網(wǎng)絡(luò)協(xié)議滑動(dòng)窗口請(qǐng)求應(yīng)答式交互時(shí)队橙,處理超時(shí)未響應(yīng)的請(qǐng)求坠陈。
如果不使用DelayQueue,那么常規(guī)的解決辦法就是:使用一個(gè)后臺(tái)線程喘帚,遍歷所有對(duì)象畅姊,挨個(gè)檢查。這種笨笨的辦法簡(jiǎn)單好用吹由,但是對(duì)象數(shù)量過多時(shí),可能存在性能問題朱嘴,檢查間隔時(shí)間不好設(shè)置倾鲫,間隔時(shí)間過大粗合,影響精確度,過小則存在效率問題乌昔。而且做不到按超時(shí)的時(shí)間順序處理隙疚。
DelayQueue基本原理
首先,這種隊(duì)列中只能存放實(shí)現(xiàn)Delayed接口的對(duì)象磕道,而此接口有兩個(gè)需要實(shí)現(xiàn)的方法供屉。最重要的就是getDelay,這個(gè)方法需要返回對(duì)象過期前的時(shí)間溺蕉。簡(jiǎn)單說伶丐,隊(duì)列在某些方法處理前,會(huì)調(diào)用此方法來判斷對(duì)象有沒有超時(shí)疯特。
其次哗魂,DelayQueue是一個(gè)BlockingQueue,其特化的參數(shù)是Delayed漓雅。(不了解BlockingQueue的同學(xué)录别,先去了解BlockingQueue再看本文)
Delayed擴(kuò)展了Comparable接口,比較的基準(zhǔn)為延時(shí)的時(shí)間值邻吞,Delayed接口的實(shí)現(xiàn)類getDelay的返回值應(yīng)為固定值(final)组题。DelayQueue內(nèi)部是使用PriorityQueue實(shí)現(xiàn)的。總結(jié)抱冷,DelayQueue的關(guān)鍵元素BlockingQueue崔列、PriorityQueue、Delayed徘层【唬可以這么說,DelayQueue是一個(gè)使用優(yōu)先隊(duì)列(PriorityQueue)實(shí)現(xiàn)的BlockingQueue趣效,優(yōu)先隊(duì)列的比較基準(zhǔn)值是時(shí)間瘦癌。本質(zhì)上即:
DelayQueue = BlockingQueue +PriorityQueue + Delayed
基本定義如下
public interface Comparable<T> {
public int compareTo(T o);
}
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
public class DelayQueue<E extends Delayed> implements BlockingQueue<E> {
private final PriorityQueue<E> q = new PriorityQueue<E>();
}
DelayQueue內(nèi)部的實(shí)現(xiàn)使用了一個(gè)優(yōu)先隊(duì)列。當(dāng)調(diào)用DelayQueue的offer方法時(shí)跷敬,把Delayed對(duì)象加入到優(yōu)先隊(duì)列q中讯私。如下:
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
q.offer(e);
if (first == null || e.compareTo(first) < 0)
available.signalAll();
return true;
} finally {
lock.unlock();
}
}
DelayQueue的take方法,把優(yōu)先隊(duì)列q的first拿出來(peek)西傀,如果沒有達(dá)到延時(shí)閥值斤寇,則進(jìn)行await處理。如下:
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(TimeUnit.NANOSECONDS);
if (delay > 0) {
long tl = available.awaitNanos(delay);
} else {
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll(); // wake up other takers
return x;
}
}
}
} finally {
lock.unlock();
}
}