Java中常用的七個(gè)阻塞隊(duì)列第二篇DelayQueue源碼介紹
Java中常用的七個(gè)阻塞隊(duì)列第二篇DelayQueue源碼介紹
通過前面兩篇文章,我們對(duì)隊(duì)列有了了解及已經(jīng)認(rèn)識(shí)了常用阻塞隊(duì)列中的三個(gè)了。本篇我們繼續(xù)介紹剩下的幾個(gè)隊(duì)列。
本文主要內(nèi)容:通過源碼學(xué)習(xí)Delayqueue及理解Dqueue并用代碼簡(jiǎn)單演示使用場(chǎng)景。
本文出自凱哥Java(kaigejava)的《凱哥Java并發(fā)系列》之《Java并發(fā)編程之隊(duì)列》系列的第三篇:《Java中常用的七個(gè)阻塞隊(duì)列第二篇DelayQueue源碼介紹》
Java中常用的幾個(gè)隊(duì)列中,阻塞隊(duì)列還有四個(gè)沒介紹。如下圖:

DelayQueue
先上總結(jié)腦圖:

編輯
來看看構(gòu)造器:

支持無參和支持直接存放一個(gè)集合的。
再來看看為什么說DQueue隊(duì)列使用的是PriorityQueue實(shí)現(xiàn)的呢?
來看看源碼:
在添加元素的offer方法源碼中,我們可以看到最終調(diào)用的是q.offer(e)這個(gè)方法的。那么q又是什么呢?我們接著跟下去:private final PriorityQueue<E> q = new PriorityQueue<E>();。發(fā)現(xiàn)q是PriorityQueue這個(gè)隊(duì)列。如下圖:

為什么說可以延時(shí)呢?
我們來看看DQueyue類的定義:
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {}
從源碼中,我們可以看到DQueue隊(duì)列中存放的元素必須要實(shí)現(xiàn)Delayed接口。
我們?cè)趤砜纯碊elayed接口的方法:

只有,long getDelay(TimeUnit unit);方法。設(shè)置等待時(shí)間。
在從隊(duì)列中獲取數(shù)據(jù)的時(shí)候會(huì)對(duì)超時(shí)時(shí)間進(jìn)行判斷的。當(dāng)超時(shí)時(shí)間小于等于0的時(shí)候,才會(huì)調(diào)用priorityQueue隊(duì)列的poll()方法。具體源碼如下:

從源碼中,我們可以看到,DQueue延時(shí)處理的:

無界怎么理解?
說DQueue無界,我們應(yīng)該從源碼中查找。DQueue隊(duì)列是基于PriorityQueue隊(duì)列來實(shí)現(xiàn)的。那么我們就來看看PriorityQueue隊(duì)列添加元素的源碼。

從上圖中,我們可以看到,在添加元素的時(shí)候offer方法會(huì)進(jìn)行判斷,當(dāng)i的值大于等于隊(duì)列的長(zhǎng)的時(shí)候,會(huì)調(diào)用grow()方法來進(jìn)行擴(kuò)容。在grow方法中,我們可以看到會(huì)使用Arrsys.copyof()方法復(fù)制一份給隊(duì)列。這樣隊(duì)列就完成了庫(kù)容。沒有大小的限制。所以說是無界的。
阻塞理解
當(dāng)隊(duì)列為空的時(shí)候,“獲取/取”元素操作將會(huì)block,被阻塞著。我們來看看源碼是怎么實(shí)現(xiàn)的。

從源碼中我們可以看到,當(dāng)從隊(duì)列中獲取元素的時(shí)候,先判斷,如果第一個(gè)元素為空的時(shí)候,就等待。當(dāng)?shù)却臅r(shí)間小于等于延時(shí)時(shí)間的話,就從隊(duì)列中poll了;如果leader不為空的話,說明當(dāng)前隊(duì)列不是隊(duì)首元素,依然await。
支持優(yōu)先級(jí)
因?yàn)樵赑Queue隊(duì)列的添加方法中,使用了comparator.compare方法。源碼如下圖:

所以通過源碼分析我們可以到得到DelayQueue如下腦圖:

使用場(chǎng)景:
DQueue非常有用的。我們利用DQueue的延時(shí)特性,可以講DQueue應(yīng)用于以下場(chǎng)景:
1:緩存的設(shè)計(jì)??梢岳肈queue保存緩存元素的有效期。使用一個(gè)線程循環(huán)的從隊(duì)列中獲取數(shù)據(jù)。一旦獲取到數(shù)據(jù),就說明緩存有效期到了。
2:定時(shí)任務(wù)調(diào)度??梢允褂肈queue保存需要執(zhí)行的任務(wù)和任務(wù)執(zhí)行的時(shí)間,一旦從DQueue中獲取到了任務(wù),就開始執(zhí)行任務(wù)了。比如TimerQueue就是使用了DelayQueue來實(shí)現(xiàn)的。
下面凱哥(凱哥Java:kaigejava)通過代碼簡(jiǎn)單演示模擬緩存過期時(shí)間的案例。
代碼演示:
需求:模擬緩存設(shè)置有效期。
說明:當(dāng)從隊(duì)列中獲取到元素,說明元素的有效期到了。
模擬緩存的對(duì)象:

構(gòu)造器:

需要注意:time=傳遞的time+當(dāng)前時(shí)間。
實(shí)現(xiàn)了Delayed接口,需要重寫getDelay和compartTo方法。
重寫方法如下:

返回的是time與當(dāng)前時(shí)間之間的差值。
compareTo方法如下:

調(diào)用方法:

來看看運(yùn)行結(jié)果:

從運(yùn)行結(jié)果中,我們可以看到,從打印出開始獲取到k1的輸出之間相差1s;K1與k2之間相差2s;K3和K2之間也相差2s.符合我們上面預(yù)設(shè)的時(shí)間差。
