Java1.8-DelayQueue源碼學(xué)習(xí)(上)(四)

一蚌吸、前言

在學(xué)習(xí)DelayQueue之前圾亏,我們先來(lái)熟悉下Queue相關(guān)的一些基礎(chǔ)接口类少。

1. Queue接口

??Queue(隊(duì)列)叙身,前面學(xué)習(xí)集合框架的時(shí)候已經(jīng)了解過(guò),是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)(FIFO, First In First Out)硫狞,同樣繼承自Collection集合信轿,隊(duì)列有頭指針head和尾指針tail,數(shù)據(jù)從隊(duì)尾入隊(duì)残吩,從對(duì)頭出隊(duì)财忽。簡(jiǎn)單介紹了隊(duì)列的概念,我們來(lái)看下Queue接口中的通用方法泣侮。

??Queue接口提供了插入即彪,獲取,移除三個(gè)方法活尊,每個(gè)方法都以兩種形式存在:一種在操作失敗時(shí)拋出異常隶校,另一種在操作失敗時(shí)返回特殊值(null或false漏益,具體取決于操作),后一種形式的插入操作是專(zhuān)門(mén)用于有容量限制的隊(duì)列的深胳;

拋出異常 返回特殊值
添加 add(e) offer(e)
刪除 remove() poll()
獲取 element() peek()

Queue接口中绰疤,共有以上6個(gè)方法:

  • add, 常規(guī)的添加元素的方法,如果添加元素的時(shí)候由于容量的限制添加失敗稠屠,則會(huì)拋出異常峦睡;
  • offer, 添加元素,當(dāng)隊(duì)列有容量限制并且隊(duì)列已滿(沒(méi)有可用空間)的時(shí)候权埠,該方法會(huì)返回false榨了,而對(duì)應(yīng)的add方法則是拋出異常;所以在使用有容量限制的隊(duì)列時(shí)攘蔽,建議使用offer方法龙屉;
  • remove, 刪除方法,刪除并返回隊(duì)列的頭部元素满俗,如果隊(duì)列為空转捕,拋出異常;
  • poll, 刪除方法唆垃, 刪除并返回隊(duì)列的頭部元素五芝,和remove方法的不同之處在于,隊(duì)列為空時(shí)辕万,remove方法拋出異常枢步,而poll方法返回null;
  • element, 返回隊(duì)列的頭部元素渐尿,但不刪除醉途;
  • peek, 返回隊(duì)列的頭部元素,但不刪除砖茸,和element方法的不同在于隘擎,隊(duì)列為空時(shí),element會(huì)拋出異常凉夯,而peek則會(huì)返回null货葬;

從上面的介紹可以看出,poll和remove劲够,element和peek方法的不同就在于當(dāng)隊(duì)列為空時(shí)的處理情況震桶。

隊(duì)列實(shí)現(xiàn)通常不允許插入null元素,雖然某些實(shí)現(xiàn)(如LinkedList)允許插入null再沧。即便在允許它的實(shí)現(xiàn)中尼夺,也不應(yīng)將null插入到Queue中,因?yàn)閚ull會(huì)被poll等方法用作特殊返回值,用來(lái)表示隊(duì)列不包含任何元素淤堵。

2. Deque接口

??Deque寝衫,被稱為雙端隊(duì)列,隊(duì)列的原則是只能一頭入隊(duì)拐邪,一頭出隊(duì)慰毅,而雙端隊(duì)列則是兩端都可以入隊(duì)和出隊(duì)的隊(duì)列。Deque擴(kuò)展自Queue扎阶,并且Deque也可以用作LIFO(后進(jìn)先出)汹胃,也就是棧的操作,所以官方建議我們應(yīng)該優(yōu)先使用該接口而不是Stack來(lái)進(jìn)行棧的操作东臀。由于前面已經(jīng)學(xué)習(xí)過(guò)着饥,這里不多講了,我們還是來(lái)看下它的方法惰赋。

Head Head Tail Tail
拋出異常 返回特殊值 拋出異常 返回特殊值
添加 addFirst(e) offerFirst(e) addLast(e) offerLast(e)
刪除 removeFirst() pollFirst() removeLast() pollLast()
獲取 getFirst() peekFirst() getLast() peekLast()

由于雙端隊(duì)列可以操作對(duì)頭和隊(duì)尾宰掉,所以針對(duì)添加、刪除赁濒、獲取這三種情況轨奄,除了Queue接口中繼承的三個(gè)方法,還有分別針對(duì)隊(duì)頭和隊(duì)尾進(jìn)行操作的特殊方法拒炎。而從Queue接口繼承的方法與Deque中的一些方法是完全等效的挪拟,對(duì)應(yīng)關(guān)系如下表所示:

Queue方法 等效Deque方法
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()

上述這些方法一般用于隊(duì)列(FIFO)操作,而該接口中還提供了一些用于棧(LIFO)操作的方法击你, 當(dāng)Deque用作棧時(shí)玉组,元素將從雙端隊(duì)列的頭部進(jìn)行操作元素。 同樣果漾,Stack中對(duì)棧操作的一些常規(guī)方法與Deque中一些方法是完全等效的球切,我們來(lái)看下對(duì)應(yīng)關(guān)系:

Stack方法 等效Deque方法
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

需要注意谷誓,peek方法绒障,無(wú)論Deque是用作隊(duì)列或時(shí)棧硬萍,該方法都是有效的尚猿。另外抡诞,該接口提供了兩種方法來(lái)刪除內(nèi)部元素:

  • removeFirstOccurrence帚戳,從雙端隊(duì)列中刪除第一次出現(xiàn)的指定元素相叁;
  • removeLastOccurrence敞映,從雙端隊(duì)列中刪除最后一次出現(xiàn)的指定元素朱嘴;

此外返奉,Deque針對(duì)的使用還提供了兩個(gè)方法:

  • pop变逃,返回棧頂?shù)脑乇啬妫簿褪菑碾p端隊(duì)列中刪除并返回第一個(gè)元素(頭部),該方法等同于 removeFirst 方法;
  • push名眉,往棧里添加元素粟矿,也就是在雙端隊(duì)列的頭部添加元素,如果沒(méi)有可用空間導(dǎo)致添加失敗時(shí)會(huì)拋出異常损拢;該方法等同于addFirst方法陌粹;

還有,Deque針對(duì)Collection 的常規(guī)操作福压,還實(shí)現(xiàn)或者提供了一些方法:

  • remove(Object)掏秩,從此雙端隊(duì)列中刪除第一次出現(xiàn)的指定元素,等同于removeFirstOccurrence方法;
  • iterator荆姆,以適當(dāng)?shù)捻樞蚍祷卮穗p端隊(duì)列中元素的迭代器蒙幻,遍歷順序是從頭部到尾部;
  • descendingIterator胆筒,以相反的順序返回此雙端隊(duì)列中元素的迭代器杆煞,順序?yàn)閺奈膊康筋^部;

最后需要注意的是腐泻,與List接口不同决乎,此接口不支持對(duì)元素的索引訪問(wèn);雖然Deque實(shí)現(xiàn)并不嚴(yán)格要求禁止插入null元素派桩,但官方強(qiáng)烈建議我們?cè)谑褂肈eque時(shí)禁止插入null元素构诚。

3. BlockingQueue接口

??BlockingQueue表示阻塞隊(duì)列,該接口擴(kuò)展了Queue接口铆惑,并提供了幾個(gè)阻塞方法范嘱,用于實(shí)現(xiàn):當(dāng)生產(chǎn)者向隊(duì)列添加元素但隊(duì)列已滿時(shí),生產(chǎn)者會(huì)被阻塞员魏;當(dāng)消費(fèi)者從隊(duì)列移除元素但隊(duì)列為空時(shí)丑蛤,消費(fèi)者會(huì)被阻塞。而相對(duì)Queue接口撕阎,該接口的方法有四種表示形式:

拋出異常 返回特殊值 阻塞 超時(shí)
添加 add(e) offer(e) put(e) offer(e, time, unit)
刪除 remove() poll() take() poll(time, unit)
獲取 element() peek()

當(dāng)然受裹,BlockingQueue不支持null值,null是用作標(biāo)記的值虏束。

BlockingQueue可以有容量大小限制棉饶,超過(guò)容量大小的時(shí)候,不能在沒(méi)有阻塞的情況下插入元素镇匀,BlockingQueue的實(shí)現(xiàn)主要用于生產(chǎn)者-消費(fèi)者隊(duì)列照藻,并且,BlockingQueue可以安全地與多個(gè)生產(chǎn)者和多個(gè)消費(fèi)者一起使用汗侵。

BlockingQueue是線程安全的幸缕,所有的方法都使用內(nèi)部鎖或者其他形式的并發(fā)控制以原子方式實(shí)現(xiàn)其效果群发,但是,除非在實(shí)現(xiàn)中另有說(shuō)明发乔,否則批量操作addAll也物,containsAll,retainAll和removeAll不一定以原子方式執(zhí)行列疗。

下面對(duì)該接口的其他方法簡(jiǎn)介再介紹下:

  • put(E)滑蚯,添加元素,該方法是阻塞方法抵栈,必要時(shí)會(huì)阻塞直到隊(duì)列的容量空間可用告材;
  • take(),刪除并返回頭部元素古劲,該方法是阻塞方法斥赋,必要時(shí)會(huì)阻塞,直到隊(duì)列中的元素可用产艾;
  • remainingCapacity()疤剑,- 返回理想情況下(沒(méi)有內(nèi)存和資源約束的情況下)此隊(duì)列剩余可以無(wú)阻塞添加元素的數(shù)量,但是闷堡,我們不能總是通過(guò)檢查remainingCapacity來(lái)判斷插入元素的嘗試是否成功隘膘,因?yàn)榭赡苡辛硪粋€(gè)線程即將插入或刪除元素的情況。
  • drainTo(Collection)杠览,- 刪除此隊(duì)列中所有元素弯菊,并將這些元素添加到給定集合中;另一個(gè)兩個(gè)參數(shù)的重載方法踱阿,則表示管钳,從該隊(duì)列中刪除最多給定數(shù)量的元素,并將這些元素添加到給定集合中软舌;
  • poll(long, TimeUnit)才漆,獲取并刪除隊(duì)列的頭部元素,超時(shí)方法佛点,在必要時(shí)會(huì)等待指定的時(shí)間以使元素可用醇滥;
  • offer(E, long, TimeUnit),添加元素恋脚,超時(shí)方法腺办,在必要時(shí)會(huì)等待指定的時(shí)間以使隊(duì)列空間可用焰手;
4. BlockingDeque接口

??BlockingDeque糟描,阻塞雙端隊(duì)列,擴(kuò)展自BlockingQueue與Deque书妻,由于是雙端隊(duì)列船响,所以它的方法針對(duì)頭部和尾部操作躬拢,各有四種形式,我們首先來(lái)看下針對(duì)頭部進(jìn)行操作的:

拋出異常 返回特殊值 阻塞 超時(shí)
添加 addFirst(e) offerFirst(e) putFirst(e) offerFirst(e, time, unit)
刪除 removeFirst() pollFirst() takeFirst() pollFirst(time, unit)
獲取 getFirst() peekFirst()

同樣见间,針對(duì)尾部的操作聊闯,就是將對(duì)應(yīng)的First修改為L(zhǎng)ast:

拋出異常 返回特殊值 阻塞 超時(shí)
添加 addLast(e) offerLast(e) putLast(e) offerLast(e, time, unit)
刪除 removeLast() pollLast() takeLast() pollLast(time, unit)
獲取 getLast() peekLast()

與BlockingQueue一樣,BlockingDeque也是線程安全的米诉,不允許存儲(chǔ)null元素菱蔬,可以有容量限制。一些從BlockingQueue接口繼承的方法與BlockingDeque方法是完全等效的史侣,我們來(lái)看下這些方法:

圖片來(lái)自JDK8-api官網(wǎng).png
5. TransferQueue接口

??TransferQueue擴(kuò)展自BlockingQueue拴泌,是一個(gè)特殊的阻塞隊(duì)列。當(dāng)我們使用BlockingQueue時(shí)惊橱,我們只需要關(guān)注的是將元素放進(jìn)隊(duì)列(如果隊(duì)列已滿蚪腐,則進(jìn)行阻塞)即可,而TransferQueue則是對(duì)BlockingQueue進(jìn)行了增強(qiáng)税朴,借助該接口我們可以實(shí)現(xiàn):生產(chǎn)者會(huì)一直阻塞直到所添加到隊(duì)列的元素被某一個(gè)消費(fèi)者所消費(fèi)(不僅僅是添加到隊(duì)列里就完事)回季。

另外,該接口截止到JDK8正林,只有一個(gè)實(shí)現(xiàn)類(lèi)LinkedTransferQueue泡一,這是個(gè)無(wú)界的阻塞隊(duì)列,后面我們會(huì)介紹到這個(gè)類(lèi)觅廓。我們先來(lái)看下他提供的方法:

  • transfer(E)瘾杭,阻塞方法,在必要的情況下會(huì)阻塞哪亿,直到元素被消費(fèi)者消費(fèi)粥烁;
    • 當(dāng)有消費(fèi)者線程阻塞等待時(shí),調(diào)用transfer方法的生產(chǎn)者線程不會(huì)將元素存入隊(duì)列蝇棉,而是直接將元素傳遞給消費(fèi)者讨阻;
    • 如果調(diào)用transfer方法的生產(chǎn)者線程發(fā)現(xiàn)沒(méi)有正在等待的消費(fèi)者線程,則會(huì)將元素入隊(duì)篡殷,然后會(huì)阻塞等待钝吮,直到有一個(gè)消費(fèi)者線程來(lái)獲取該元素;
    • 在隊(duì)列中已有元素的情況下板辽,調(diào)用transfer方法奇瘦,還可以確保隊(duì)列中被傳遞元素之前的所有元素都能被處理;
  • tryTransfer(E)劲弦,當(dāng)調(diào)用tryTransfer方法時(shí)耳标,如果有消費(fèi)者正在等待接收元素,直接將元素傳送給消費(fèi)者邑跪;如果沒(méi)有次坡,則會(huì)立即返回false呼猪。該方法和transfer方法的區(qū)別在于tryTransfer方法無(wú)論消費(fèi)者是否接收,方法立即返回砸琅,元素不會(huì)入隊(duì)宋距;而transfer方法必須等到消費(fèi)者消費(fèi)后才返回;
  • tryTransfer(E, long, TimeUnit)症脂,超時(shí)方法谚赎,如果有消費(fèi)者正在等待接收元素,則理解將元素給消費(fèi)者诱篷;如果在這一段超時(shí)時(shí)間內(nèi)還沒(méi)有消費(fèi)者消費(fèi)元素沸版,則返回false;如果在超時(shí)時(shí)間內(nèi)消費(fèi)了元素兴蒸,則返回true视粮;
  • hasWaitingConsumer(),判斷是否有消費(fèi)者正在等待接收元素橙凳,如果至少有一個(gè)消費(fèi)者正在等待通過(guò)BlockingQueue.take()或定時(shí)輪詢接收元素蕾殴,則返回true;
  • getWaitingConsumerCount()岛啸,返回通過(guò)BlockingQueue.take()或定時(shí)輪詢等待接收元素的消費(fèi)者數(shù)量的估計(jì)值钓觉,該值是一個(gè)瞬時(shí)值,可能不準(zhǔn)確坚踩,該值可用于監(jiān)控使用荡灾,但不適合用于程序中同步控制;

最后瞬铸,我們簡(jiǎn)單看一個(gè)小例子:

public static void testTransferQueue() throws InterruptedException {
    TransferQueue<String> transferQueue = new LinkedTransferQueue<>();
    transferQueue.add("hello");
    new Thread(() -> {
        try {
            transferQueue.transfer("world");
            System.out.println("new Thread ->> ");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();

    String str = transferQueue.take();
    // output hello
    System.out.println("finish ->> " + str);
    // 以下代碼可以先注釋掉批幌,看一下運(yùn)行情況,再去掉注釋運(yùn)行
    // str = transferQueue.take();
    // System.out.println("finish ->> " + str);
}

本文主要參考自:
JDK8官方API:https://docs.oracle.com/javase/8/docs/api/
Java 7中的TransferQueue - 并發(fā)編程網(wǎng)
Java多線程進(jìn)階(三八)—— J.U.C之collections框架:LinkedTransferQueue

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嗓节,一起剝皮案震驚了整個(gè)濱河市荧缘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拦宣,老刑警劉巖截粗,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鸵隧,居然都是意外死亡绸罗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)豆瘫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)珊蟀,“玉大人,你說(shuō)我怎么就攤上這事靡羡∠德澹” “怎么了俊性?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵略步,是天一觀的道長(zhǎng)描扯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)趟薄,這世上最難降的妖魔是什么绽诚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮杭煎,結(jié)果婚禮上恩够,老公的妹妹穿的比我還像新娘。我一直安慰自己羡铲,他們只是感情好蜂桶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著也切,像睡著了一般扑媚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雷恃,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天疆股,我揣著相機(jī)與錄音,去河邊找鬼倒槐。 笑死旬痹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讨越。 我是一名探鬼主播两残,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼把跨!你這毒婦竟也來(lái)了磕昼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤节猿,失蹤者是張志新(化名)和其女友劉穎票从,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滨嘱,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡峰鄙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了太雨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吟榴。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖囊扳,靈堂內(nèi)的尸體忽然破棺而出吩翻,到底是詐尸還是另有隱情兜看,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布狭瞎,位于F島的核電站细移,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熊锭。R本人自食惡果不足惜弧轧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碗殷。 院中可真熱鬧精绎,春花似錦、人聲如沸锌妻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仿粹。三九已至搁吓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牍陌,已是汗流浹背擎浴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毒涧,地道東北人贮预。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像契讲,于是被迫代替她去往敵國(guó)和親仿吞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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