概述
DelayQueue是一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部是延遲期滿後保存時間最長的Delayed 元素。
DelayQueue阻塞隊列在我們系統開發中也常常會用到,例如:緩存系統的設計,緩存中的對象,超過了空閒時間,需要從緩存中移出;任務調度系統,能夠準確的把握任務的執行時間。我們可能需要通過線程處理很多時間上要求很嚴格的數據,如果使用普通的線程,我們就需要遍歷所有的對象,一個一個的檢 查看數據是否過期等,首先這樣在執行上的效率不會太高,其次就是這種設計的風格也大大的影響了數據的精度。一個需要12:00點執行的任務可能12:01 才執行,這樣對數據要求很高的系統有更大的弊端。由此我們可以使用DelayQueue。
爲了具有調用行爲,存放到DelayDeque的元素必須繼承Delayed接口。Delayed接口使對象成爲延遲對象,它使存放在DelayQueue類中的對象具有了激活日期。該接口強制執行下列兩個方法。
- CompareTo(Delayed o):Delayed接口繼承了Comparable接口,因此有了這個方法。
- getDelay(TimeUnit unit):這個方法返回到激活日期的剩餘時間,時間單位由單位參數指定。
Delayed,一種混合風格的接口,用來標記那些應該在給定延遲時間之後執行的對象。此接口的實現必須定義一個 compareTo 方法,該方法提供與此接口的 getDelay 方法一致的排序。
簡單的延時隊列要有三部分:第一實現了Delayed接口的消息體、第二消費消息的消費者、第三存放消息的延時隊列
public class DelayQueueTest {
static class Message implements Delayed
{
private int id;
private String body; // 消息內容
private long excuteTime;// 延遲時長,這個是必須的屬性因爲要按照這個判斷延時時長。
public int getId() {
return id;
}
public String getBody() {
return body;
}
public long getExcuteTime() {
return excuteTime;
}
public Message(int id, String body, long delayTime) {
this.id = id;
this.body = body;
this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
}
// 自定義實現比較方法返回 1 0 -1三個參數
@Override
public int compareTo(Delayed delayed) {
Message msg = (Message) delayed;
return Integer.valueOf(this.id) > Integer.valueOf(msg.id) ? 1
: (Integer.valueOf(this.id) < Integer.valueOf(msg.id) ? -1 : 0);
}
// 延遲任務是否到時就是按照這個方法判斷如果返回的是負數則說明到期否則還沒到期
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
}
public static class Consumer implements Runnable {
// 延時隊列 ,消費者從其中獲取消息進行消費
private DelayQueue<Message> queue;
public Consumer(DelayQueue<Message> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Message take = queue.take();
System.out.println("消費消息id:" + take.getId() + " 消息體:" + take.getBody());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// 創建延時隊列
DelayQueue<Message> queue = new DelayQueue<Message>();
// 添加延時消息,m1 延時3s
Message m1 = new Message(1, "world", 3000);
// 添加延時消息,m2 延時10s
Message m2 = new Message(2, "hello", 10000);
//將延時消息放到延時隊列中
queue.offer(m2);
queue.offer(m1);
// 啓動消費線程 消費添加到延時隊列中的消息,前提是任務到了延期時間
ExecutorService exec = Executors.newFixedThreadPool(1);
exec.execute(new Consumer(queue));
exec.shutdown();
}
}
實現
DelayQueue是基於PriorityQueue來實現的。
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>();
...
}
只會返回過期的元素
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
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)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}