阻塞佇列BlockingQueue(三)--DelayQueue

eluanshi12發表於2018-12-20

DelayQueue

支援延時獲取元素的無界阻塞佇列
內部採用PriorityQueue與ReentrantLock實現。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {

    private transient  final ReentrantLock lock = new ReentrantLock();
    private transient  final Condition available = lock.newCondition();
    private final PriorityQueue<E> q = new PriorityQueue<E>();
    ...
}

佇列中元素必須實現Delayed介面,Delayed介面又繼承了Comparable介面,原因在於DelayQueue內部元素需要排序,一般情況按過期時間優先順序排序。

public interface Delayed extends Comparable<Delayed> {	
    long getDelay(TimeUnit unit);
}

應用場景

  • 快取系統的設計:可以用DelayQueue儲存快取元素的有效期,使用一個執行緒迴圈查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示快取有效期到了。
  • 定時任務排程:使用DelayQueue儲存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,比如TimerQueue就是使用DelayQueue實現的。

實現Delayed介面

第一步:在物件建立的時候,初始化基本資料。使用time記錄當前物件延遲到什麼時候可以使用,使用sequenceNumber來標識元素在佇列中的先後順序。

private static final AtomicLong sequencer = new AtomicLong(0);
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
	super(r, result);
	this.time = ns;
	this.period = period;
	this.sequenceNumber = sequencer.getAndIncrement();
}

第二步:實現(Delayed 介面的)getDelay方法返回當前元素還需要延時多長時間,單位是納秒。

public long getDelay(TimeUnit unit) {
	return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}

第三步:如何實現compareTo方法來指定元素的順序。例如,讓延時時間最長的放在佇列的末尾。
compareTo方法實現

如何實現延時阻塞佇列

當消費者從佇列裡獲取元素時,如果元素沒有達到延時時間,就阻塞當前執行緒。
實現延時阻塞佇列

leader變數是一個等待獲取佇列頭部元素的執行緒。

  • 如果leader!=null,表示已經有執行緒在等待獲取佇列的頭元素。所以,使用await()方法讓當前執行緒等待訊號。
  • 如果leader==null,則把當前執行緒設定成leader,並使用awaitNanos()方法讓當前執行緒等待接收訊號或等待delay時間。

相關文章