多線程(二十一、阻塞隊列-DelayQueue)

DelayQueue簡介

DelayQueue中的所有元素必須實現(xiàn)Delayed接口,還必須實現(xiàn)還實現(xiàn)了Comparable接口。

多線程(二十一、阻塞隊列-DelayQueue)

成都創(chuàng)新互聯(lián)公司專注于克拉瑪依區(qū)企業(yè)網(wǎng)站建設(shè),響應式網(wǎng)站建設(shè),成都商城網(wǎng)站開發(fā)??死斠绤^(qū)網(wǎng)站建設(shè)公司,為克拉瑪依區(qū)等地區(qū)提供建站服務。全流程按需制作網(wǎng)站,專業(yè)設(shè)計,全程項目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務

1、DelayQueue是×××阻塞隊列
2、隊列中的元素必須實現(xiàn)Delayed接口,只有當該對象的getDalay方法返回的剩余時間≤0時才會出隊。
3、剩余時間最小的元素就在堆頂,每次出隊其實就是刪除剩余時間≤0的最小元素。

DelayQueue構(gòu)造

基于PriorityQueue的堆實現(xiàn)隊列,保證最先失效的在堆頂。

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>();

    /**
     * leader線程是首個嘗試出隊元素(隊列不為空)但被阻塞的線程.
     * 該線程會限時等待(隊首元素的剩余有效時間),用于喚醒其它等待線程
     * 這個很關(guān)鍵,當隊列為空或者堆頂元素還沒達到出隊時間時,所有線程都會阻塞的,要保證有線程可以喚醒,而leader線程就是這個首先自己喚醒自己的線程,然后才能繼續(xù)喚醒其他線程。
     */
    private Thread leader = null;

    /**
     * 出隊線程條件隊列, 當有多個線程, 會在此條件隊列上等待.
     */
    private final Condition available = lock.newCondition();

}
leader字段很關(guān)鍵,DelayQueue每次只會出隊一個過期的元素,如果隊首元素沒有過期,就會阻塞出隊線程,讓線程在available這個條件隊列上無限等待。

為了提升性能,DelayQueue并不會讓所有出隊線程都無限等待,而是用leader保存了第一個嘗試出隊的線程,該線程的等待時間是隊首元素的剩余有效期。這樣,一旦leader線程自己喚醒(此時隊首元素也失效了),就可以出隊成功,然后喚醒一個其它在available條件隊列上等待的線程。之后,會重復上一步,新喚醒的線程可能取代成為新的leader線程。

入隊-put

/**
 * 入隊一個指定元素e.
 * 由于是×××隊列, 所以該方法并不會阻塞線程.
 */
public void put(E e) {
    offer(e);
}

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        q.offer(e);                  // 調(diào)用PriorityQueue的offer方法
                // 如果入隊元素在隊首, 則喚醒一個出隊線程
                // 當首次入隊元素時,需要喚醒一個出隊線程
                // 因為此時可能已有出隊線程在空隊列上等待了
                //如果不喚醒,會導致出隊線程永遠無法執(zhí)行。
        if (q.peek() == e) {    
            leader = null;
            available.signal();
        }
        return true;
    } finally {
        lock.unlock();
    }
}

出隊-take

1.隊列為空,直接阻塞出隊線程,在available條件隊列等待
2.隊列非空,還要看隊首元素的有效期,如果隊首元素過期了,那直接出隊就行了;如果隊首元素未過期,就要看leader是否為空,如果不是,就無限等待,如果是,則自己成為leader,限時等待。

/**
 * 隊首出隊元素.
 * 如果隊首元素(堆頂)未到期或隊列為空, 則阻塞線程.
 */
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (; ; ) { //出隊是一個自旋操作
            E first = q.peek();     // 讀取隊首元素
            if (first == null)      // CASE1: 隊列為空, 直接阻塞
                available.await();
            else {                  // CASE2: 隊列非空
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0)       // CASE2.0: 隊首元素已過期,直接出隊
                    return q.poll();

                // 執(zhí)行到此處說明隊列非空, 且隊首元素未過期
                first = null;
                if (leader != null)           // CASE2.1: 已存在leader線程
                    available.await();      // 無限期阻塞當前線程
                else {                            // CASE2.2: 不存在leader線程
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;    // 將當前線程置為leader線程
                    try {
                        available.awaitNanos(delay);  // 阻塞當前線程(限時等待剩余有效時間)
                    } finally {
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        if (leader == null && q.peek() != null)  // 不存在leader線程,而且隊列不空, 則喚醒一個其它出隊線程,防止任務無法執(zhí)行。
            available.signal();
        lock.unlock();
    }
}

總結(jié)

1、DelayQueue是阻塞隊列中非常有用的一種隊列,經(jīng)常被用于緩存或定時任務等的設(shè)計。
2、ScheduledThreadPoolExecutor.DelayedWorkQueue就是一種延時阻塞隊列。

名稱欄目:多線程(二十一、阻塞隊列-DelayQueue)
網(wǎng)站地址:http://bm7419.com/article12/gegogc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司品牌網(wǎng)站制作、響應式網(wǎng)站、移動網(wǎng)站建設(shè)、虛擬主機、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司