死磕 java集合之DelayQueue源碼分析

問題

(1)DelayQueue是阻塞隊列嗎脆栋?

(2)DelayQueue的實現(xiàn)方式倦卖?

(3)DelayQueue主要用于什么場景?

簡介

DelayQueue是java并發(fā)包下的延時阻塞隊列椿争,常用于實現(xiàn)定時任務(wù)怕膛。

繼承體系

從繼承體系可以看到,DelayQueue實現(xiàn)了BlockingQueue秦踪,所以它是一個阻塞隊列褐捻。

另外椅邓,DelayQueue還組合了一個叫做Delayed的接口板壮,DelayQueue中存儲的所有元素必須實現(xiàn)Delayed接口绰精。

那么卿樱,Delayed是什么呢繁调?

publicinterfaceDelayedextendsComparable{longgetDelay(TimeUnit unit);}

Delayed是一個繼承自Comparable的接口,并且定義了一個getDelay()方法烤送,用于表示還有多少時間到期帮坚,到期了應(yīng)返回小于等于0的數(shù)值。

源碼分析

主要屬性

// 用于控制并發(fā)的鎖privatefinaltransientReentrantLock lock =newReentrantLock();// 優(yōu)先級隊列privatefinalPriorityQueue q =newPriorityQueue();// 用于標(biāo)記當(dāng)前是否有線程在排隊(僅用于取元素時)privateThread leader =null;// 條件阅悍,用于表示現(xiàn)在是否有可取的元素privatefinalCondition available = lock.newCondition();

從屬性我們可以知道,延時隊列主要使用優(yōu)先級隊列來實現(xiàn)寻行,并輔以重入鎖和條件來控制并發(fā)安全拌蜘。

因為優(yōu)先級隊列是無界的,所以這里只需要一個條件就可以了举娩。

還記得優(yōu)先級隊列嗎?點(diǎn)擊鏈接直達(dá)【死磕 java集合之PriorityQueue源碼分析

主要構(gòu)造方法

publicDelayQueue(){}publicDelayQueue(Collection<? extends E> c){this.addAll(c);}

構(gòu)造方法比較簡單骄噪,一個默認(rèn)構(gòu)造方法,一個初始化添加集合c中所有元素的構(gòu)造方法滔韵。

入隊

因為DelayQueue是阻塞隊列陪蜻,且優(yōu)先級隊列是無界的,所以入隊不會阻塞不會超時症昏,因此它的四個入隊方法是一樣的肝谭。

publicbooleanadd(E e){returnoffer(e);}publicvoidput(E e){? ? offer(e);}publicbooleanoffer(E e,longtimeout, TimeUnit unit){returnoffer(e);}publicbooleanoffer(E e){finalReentrantLock lock =this.lock;? ? lock.lock();try{? ? ? ? q.offer(e);if(q.peek() == e) {? ? ? ? ? ? leader =null;? ? ? ? ? ? available.signal();? ? ? ? }returntrue;? ? }finally{? ? ? ? lock.unlock();? ? }}

入隊方法比較簡單:

(1)加鎖;

(2)添加元素到優(yōu)先級隊列中医寿;

(3)如果添加的元素是堆頂元素,就把leader置為空沟突,并喚醒等待在條件available上的線程惠拭;

(4)解鎖棒呛;

出隊

因為DelayQueue是阻塞隊列,所以它的出隊有四個不同的方法趋观,有拋出異常的皱坛,有阻塞的,有不阻塞的抹沪,有超時的融欧。

我們這里主要分析兩個,poll()和take()方法欠肾。

publicEpoll(){finalReentrantLock lock =this.lock;? ? lock.lock();try{? ? ? ? E first = q.peek();if(first ==null|| first.getDelay(NANOSECONDS) >0)returnnull;elsereturnq.poll();? ? }finally{? ? ? ? lock.unlock();? ? }}

poll()方法比較簡單:

(1)加鎖;

(2)檢查第一個元素瑟慈,如果為空或者還沒到期葛碧,就返回null;

(3)如果第一個元素到期了就調(diào)用優(yōu)先級隊列的poll()彈出第一個元素乳绕;

(4)解鎖翩隧。

publicEtake()throwsInterruptedException{finalReentrantLock lock =this.lock;? ? lock.lockInterruptibly();try{for(;;) {// 堆頂元素E first = q.peek();// 如果堆頂元素為空,說明隊列中還沒有元素雷酪,直接阻塞等待if(first ==null)? ? ? ? ? ? ? ? available.await();else{// 堆頂元素的到期時間longdelay = first.getDelay(NANOSECONDS);// 如果小于0說明已到期,直接調(diào)用poll()方法彈出堆頂元素if(delay <=0)returnq.poll();// 如果delay大于0 吩跋,則下面要阻塞了// 將first置為空方便gc锌钮,因為有可能其它元素彈出了這個元素// 這里還持有著引用不會被清理first =null;// don't retain ref while waiting// 如果前面有其它線程在等待,直接進(jìn)入等待if(leader !=null)? ? ? ? ? ? ? ? ? ? available.await();else{// 如果leader為null旺韭,把當(dāng)前線程賦值給它Thread thisThread = Thread.currentThread();? ? ? ? ? ? ? ? ? ? leader = thisThread;try{// 等待delay時間后自動醒過來// 醒過來后把leader置空并重新進(jìn)入循環(huán)判斷堆頂元素是否到期// 這里即使醒過來后也不一定能獲取到元素// 因為有可能其它線程先一步獲取了鎖并彈出了堆頂元素// 條件鎖的喚醒分成兩步值漫,先從Condition的隊列里出隊// 再入隊到AQS的隊列中,當(dāng)其它線程調(diào)用LockSupport.unpark(t)的時候才會真正喚醒// 關(guān)于AQS我們后面會講的^^available.awaitNanos(delay);? ? ? ? ? ? ? ? ? ? }finally{// 如果leader還是當(dāng)前線程就把它置為空悔政,讓其它線程有機(jī)會獲取元素if(leader == thisThread)? ? ? ? ? ? ? ? ? ? ? ? ? ? leader =null;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? }finally{// 成功出隊后槽地,如果leader為空且堆頂還有元素集畅,就喚醒下一個等待的線程if(leader ==null&& q.peek() !=null)// signal()只是把等待的線程放到AQS的隊列里面,并不是真正的喚醒a(bǔ)vailable.signal();// 解鎖赦颇,這才是真正的喚醒lock.unlock();? ? }}

take()方法稍微要復(fù)雜一些:

(1)加鎖媒怯;

(2)判斷堆頂元素是否為空,為空的話直接阻塞等待鳖敷;

(3)判斷堆頂元素是否到期定踱,到期了直接調(diào)用優(yōu)先級隊列的poll()彈出元素;

(4)沒到期至扰,再判斷前面是否有其它線程在等待敢课,有則直接等待;

(5)前面沒有其它線程在等待鞭盟,則把自己當(dāng)作第一個線程等待delay時間后喚醒筝野,再嘗試獲取元素;

(6)獲取到元素之后再喚醒下一個等待的線程歇竟;

(7)解鎖焕议;

使用方法

說了那么多唤锉,是不是還是不知道怎么用呢?那怎么能行,請看下面的案例:

publicclassDelayQueueTest{publicstaticvoidmain(String[] args){? ? ? ? DelayQueue queue =newDelayQueue<>();longnow = System.currentTimeMillis();// 啟動一個線程從隊列中取元素newThread(()->{while(true) {try{// 將依次打印1000籽慢,2000,5000届惋,7000脑豹,8000System.out.println(queue.take().deadline - now);? ? ? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }).start();// 添加5個元素到隊列中queue.add(newMessage(now +5000));? ? ? ? queue.add(newMessage(now +8000));? ? ? ? queue.add(newMessage(now +2000));? ? ? ? queue.add(newMessage(now +1000));? ? ? ? queue.add(newMessage(now +7000));? ? }}classMessageimplementsDelayed{longdeadline;publicMessage(longdeadline){this.deadline = deadline;? ? }@OverridepubliclonggetDelay(TimeUnit unit){returndeadline - System.currentTimeMillis();? ? }@OverridepublicintcompareTo(Delayed o){return(int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));? ? }@OverridepublicStringtoString(){returnString.valueOf(deadline);? ? }}

是不是很簡單拌牲,越早到期的元素越先出隊拍埠。

總結(jié)

(1)DelayQueue是阻塞隊列嬉探;

(2)DelayQueue內(nèi)部存儲結(jié)構(gòu)使用優(yōu)先級隊列甲馋;

(3)DelayQueue使用重入鎖和條件來控制并發(fā)安全;

(4)DelayQueue常用于定時任務(wù)痊远;

寫在最后:

碼字不易看到最后了碧聪,那就點(diǎn)個關(guān)注唄,只收藏不點(diǎn)關(guān)注的都是在耍流氓滞造!關(guān)注并私信我“架構(gòu)”,免費(fèi)送一些Java架構(gòu)資料买窟,先到先得!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市径簿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌译蒂,老刑警劉巖哑芹,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件末购,死亡現(xiàn)場離奇詭異虎谢,居然都是意外死亡盟榴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門婴噩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來擎场,“玉大人,你說我怎么就攤上這事几莽⊙赴欤” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長笑跛,這世上最難降的妖魔是什么陈哑? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任盾计,我火速辦了婚禮,結(jié)果婚禮上桶唐,老公的妹妹穿的比我還像新娘坯约。我一直安慰自己滥玷,他們只是感情好陷虎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布塘安。 她就那樣靜靜地躺著砸脊,像睡著了一般绢陌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓶竭,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天雌芽,我揣著相機(jī)與錄音,去河邊找鬼专钉。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蟹但,長吁一口氣:“原來是場噩夢啊……” “哼向族!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤膀息,失蹤者是張志新(化名)和其女友劉穎襟沮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體大审,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圈澈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年囚戚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悴务。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡睹限,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讯檐,到底是詐尸還是另有隱情羡疗,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布别洪,位于F島的核電站叨恨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挖垛。R本人自食惡果不足惜痒钝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痢毒。 院中可真熱鬧送矩,春花似錦、人聲如沸哪替。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夷家。三九已至蒸其,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間库快,已是汗流浹背义屏。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工蜂大, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝶怔。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓踢星,卻偏偏與公主長得像估盘,于是被迫代替她去往敵國和親魏烫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 引言 JDK中除了上文提到的各種并發(fā)容器瓶殃,還提供了豐富的阻塞隊列遥椿。阻塞隊列統(tǒng)一實現(xiàn)了BlockingQueue接口...
    小刀愛編程閱讀 562評論 0 0
  • DelayQueue源碼學(xué)習(xí) DelayQueue是一個提供過期時間的隊列修壕,只返回消耗完等待時間的元素遏考,暫時還沒發(fā)...
    senninha閱讀 447評論 0 0
  • 本文將會對DelayQueue做一個簡單的介紹灌具,并提供部分源碼的分析。 DelayQueue的特性基本上由Bloc...
    逍遙jc閱讀 1,420評論 2 2
  • 第一次地面課。體驗還不錯娃肿,老師講得很吸引人珠十,各位。晒杈。孔厉。嗯。粪般。姐姐們? ﹏ ?也很友善亩歹。小小一間屋子里能人不少,幸會...
    牧狼人布川酷閱讀 150評論 0 0
  • 在兒子的幼兒園母校我們養(yǎng)成了愛讀書愛閱讀的好習(xí)慣舅柜,那個時候有繪本漂流活動,每天都可以從幼兒園帶回來孩子從未看過的書...
    野地百合_35b6閱讀 364評論 1 2