首頁 > 軟體

詳解Java執行緒池佇列中的延遲佇列DelayQueue

2022-12-05 14:00:16

在阻塞隊裡中,除了對元素進行增加和刪除外,我們可以把元素的刪除做一個延遲的處理,即使用DelayQueue的方法。本文就來和大家聊聊Java執行緒池佇列中的DelayQueue—延遲佇列 

public enum QueueTypeEnum {
    ARRAY_BLOCKING_QUEUE(1, "ArrayBlockingQueue"),
    LINKED_BLOCKING_QUEUE(2, "LinkedBlockingQueue"),
    DELAY_QUEUE(3, "DelayQueue"),
    PRIORITY_BLOCKING_QUEUE(4, "PriorityBlockingQueue"),
    SYNCHRONOUS_QUEUE(5, "SynchronousQueue"),
    LINKED_TRANSFER_QUEUE(6, "LinkedTransferQueue"),
    LINKED_BLOCKING_DEQUE(7, "LinkedBlockingDeque"),
    VARIABLE_LINKED_BLOCKING_QUEUE(8, "VariableLinkedBlockingQueue"),
    MEMORY_SAFE_LINKED_BLOCKING_QUEUE(9, "MemorySafeLinkedBlockingQueue");
}

DelayQueue延遲佇列

類似於PriorityBlockingQueue,是二元堆積實現的無界優先順序阻塞佇列。要求元素都實現Delayed 介面,通過執行時延從佇列中提取任務,只有在延遲期滿後才能從中提取元素。DelayQueue的泛型引數需要實現Delayed介面,Delayed介面繼承了Comparable介面,DelayQueue內部使用非執行緒安全的優先佇列(PriorityQueue),並使用Leader/Followers模式,最小化不必要的等待時間。DelayQueue不允許包含null元素。

public interface Delayed extends Comparable<Delayed> {

    /**
     * 返回與此物件關聯的剩餘延遲(給定的時間單位)。
     * @param unit 時間單位
     * @返回剩餘延遲;零值或負值表示 延遲已過期
     */
    long getDelay(TimeUnit unit);
}

DelayQueue使用場景

快取系統的設計:可以用DelayQueue儲存快取元素的有效期,使用一個執行緒迴圈查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示快取有效期到了。

定時任務排程:使用DelayQueue儲存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,從比如TimerQueue就是使用DelayQueue實現的。

DelayQueue屬性

//可重入同步鎖
private final transient ReentrantLock lock = new ReentrantLock();

//DelayQueue的實現依賴於PriorityQueue(優先佇列)
private final PriorityQueue<E> q = new PriorityQueue<E>();

//第一個等待某個延時物件的執行緒,在延時物件還沒有到期時其他執行緒看到這個leader不為null,那麼就直接wait
//主要是為了避免大量執行緒在同一時間點喚醒,導致大量的競爭,反而影響效能
private Thread leader = null;

//條件佇列,用於wait執行緒
private final Condition available = lock.newCondition();

DelayQueue構造方法

//從上面屬性就可以看出,DelayQueue採用了餓漢模式,呼叫構造方法即建立了佇列範例
public DelayQueue() {}

/**
 * 建立一個DelayQueue,最初包含給定的Collection範例集合。
 * @param c 最初包含的元素集合
 */
public DelayQueue(Collection<? extends E> c) {
    this.addAll(c);
}

實現Delayed介面使用範例

class MyDelay<T> implements Delayed {

    long delayTime; // 延遲時間
    long expire; // 過期時間
    T data;

    public MyDelay(long delayTime, T t) {
        this.delayTime = delayTime;
        // 過期時間 = 當前時間 + 延遲時間
        this.expire = System.currentTimeMillis() + delayTime;
        data = t;
    }

    /**
     * 剩餘時間 = 到期時間 - 當前時間
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    /**
     * 優先順序規則:兩個任務比較,時間短的優先執行
     */
    @Override
    public int compareTo(Delayed o) {
        long f = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
        return (int) f;
    }


    @Override
    public String toString() {
        return "delayTime=" + delayTime +
                ", expire=" + expire +
                ", data=" + data;
    }
}

public class DelayQueueDemo {
    static BlockingQueue<Delayed> queue = new DelayQueue();
    public static void main(String[] args) throws InterruptedException {
        queue.add(new MyDelay(8, "第一次新增任務"));
        queue.add(new MyDelay(3, "第二次新增任務"));
        queue.add(new MyDelay(5, "第三次新增任務"));

        while (!queue.isEmpty()) {
            Delayed delayed = queue.take();
            System.out.println(delayed);
        }
    }
}

DelayQueue總結

DelayQueue其實採用了裝飾器模式,在對PriorityQueue進行包裝下增加了延時時間獲取元素的功能,其主要特點歸納如下:

  • DelayQueue是一個無界阻塞佇列,佇列內部使用PriorityQueue來實現
  • 進入佇列的元素必須實現Delayed介面,在建立元素時可以指定多久才能從佇列中獲取當前元素,只有在延遲期滿時才能從中提取元素
  • 該佇列頭部是延遲期滿後儲存時間最長的Delayed元素
  • 如果沒有延遲未過期元素,且佇列沒有頭部,並且poll將返回null
  • 當一個元素的getDelay(TimeUnit.NANOSECONDS)方法返回一個小於等於0的值時,表示該元素已過期
  • 無法使用poll或take移除未到期的元素,也不會將這些元素作為正常元素對待;例如:size方法返回到期和未到期元素的計數之和
  • 此佇列不允許使用null元素

到此這篇關於詳解Java執行緒池佇列中的延遲佇列DelayQueue的文章就介紹到這了,更多相關Java延遲佇列DelayQueue內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com