在談到DelayQueue的使用和原理的時候,我們首先介紹一下DelayQueue,DelayQueue是一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部是延遲期滿後儲存時間最長的Delayed 元素。
DelayQueue阻塞隊列在我們系統開發中也常常會用到,例如:緩衝系統的設計,緩衝中的對象,超過了空閑時間,需要從緩衝中移出;任務調度系統,能夠準確的把握任務的執行時間。我們可能需要通過線程處理很多時間上要求很嚴格的資料,如果使用普通的線程,我們就需要遍曆所有的對象,一個一個的檢查看資料是否到期等,首先這樣在執行上的效率不會太高,其次就是這種設計的風格也大大的影響了資料的精度。一個需要12:00點執行的任務可能12:01才執行,這樣對資料要求很高的系統有更大的弊端。由此我們可以使用DelayQueue。
延遲堵塞隊列的實現原理類似於TimerTask。下面假設對超過一定時間的問題的狀態修改為實現目標來完成。首先我們利用普通線程對n個問題,然後比對問題的建立時間和現在的時間比較然後來修改問題的狀態: 我們可以定時的每兩分鐘查看一次資料對到期的問題的狀態進行修改,很顯然這種情況對資料的準確性把握不夠,其次是不斷的查詢資料庫效率問題。 將問題放在普通的隊列中,這種情況需要我們不斷的遍曆改隊列達到修改狀態的功能。DelayQueue可以很好的完成這樣的工作。
1.首先建立一個Task類,要求實現Delayed,這點類似於TimerTask。設定成員變數“到期時間”,“以及問題對象”並建立構造方法 如下:
public class Task implements Delayed {/** * 到期時間 */private final long time;/** * 問題對象 */private final Question question;private static final AtomicLong atomic = new AtomicLong(0);private final long n;public Task(long timeout, Question question) {this.time = System.nanoTime() + timeout;this.question = question;this.n = atomic.getAndIncrement();}/** * 返回與此對象相關的剩餘延遲時間,以給定的時間單位表示 */@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(this.time - System.nanoTime() , TimeUnit.NANOSECONDS);}
2.建立Job類,該類用於執行Task,在這裡申明一個線程,該線程用於用隊列中取出Task,並將到期的問題狀態修改。DelayQueue類的take()方法用於擷取並移除此隊列的頭部,在可從此隊列獲得到期延遲的元素之前一直等待。如果Task中timeout大於當前的時間則可以通過take()取出到期的對象,如果沒有則該隊列一直等待。
/** * 守護線程 */private Thread daemonThread;public Job(){Runnable daemonTask = new DaemonThread();daemonThread = new Thread(daemonTask);daemonThread.setDaemon(true);daemonThread.setName("Cache Daemon");daemonThread.start();}//執行線程class DaemonThread implements Runnable{@Overridepublic void run() {execute();}}public void execute(){System.out.println("start");while(true) {try {//從延遲隊列中取值,如果沒有對象到期則隊列一直等待,Task t1 = t.take();if (t1 != null) {//修改問題的狀態Question q = t1.getQuestion();System.out.println(q);questionDao.setQuestionSolved(q.getId(), 5600);}} catch (Exception e) {e.printStackTrace();break;}}}
3.建立延遲隊列將Task加入到延遲隊列中,如下:
/** * 建立一個最初為空白的新 DelayQueue */private DelayQueue t = new DelayQueue();/** * 新增工作, * time 延遲時間 * q 問題 * 使用者為問題設定延遲時間 */public void put(long time,Question q){//轉換成nslong nanoTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.SECONDS);//建立一個任務Task k = new Task(nanoTime,q);//將任務放在延遲的隊列中t.put(k);} 4.測試,檢驗延遲隊列是否可以正常運行。
List<Question> qs = new ArrayList<Question>(); //從資料庫中取出10條資料 for (int i = 1; i <= 10; i++) {Question q = questionDao.queryQuestionById(i) ;qs.add(q); } //設定資料庫中資料的到期時間 for (int k = 1; k <= 10; k++) {j.put(k*5, qs.get(k - 1)); } Thread.sleep(200000);
(轉載自:http://express.ruanko.com/ruanko-express_69/tech-overnight5.html)