并發(fā)編程 – Concurrent 用戶指南( 一)

1. java.util.concurrent – Java 并發(fā)工具包

Java 5 添加了一個新的包到 Java 平臺绽昏,java.util.concurrent 包备埃。這個包包含有一系列能夠讓 Java 的并發(fā)編程變得更加簡單輕松的類税迷。在這個包被添加以前,你需要自己去動手實(shí)現(xiàn)自己的相關(guān)工具類伞矩。

本文我將帶你一一認(rèn)識 java.util.concurrent 包里的這些類赫编,然后你可以嘗試著如何在項(xiàng)目中使用它們蛤奢。本文中我將使用 Java 6 版本,我不確定這和 Java 5 版本里的是否有一些差異饵骨。我不會去解釋關(guān)于 Java 并發(fā)的核心問題 – 其背后的原理翘悉,也就是說,如果你對那些東西感興趣居触,參考《Java 并發(fā)指南》妖混。

半成品

本文很大程度上還是個 “半成品”,所以當(dāng)你發(fā)現(xiàn)一些被漏掉的類或接口時轮洋,請耐心等待制市。在我空閑的時候會把它們加進(jìn)來的。

2. 阻塞隊(duì)列 BlockingQueue

java.util.concurrent 包里的 BlockingQueue 接口表示一個線程安放入和提取實(shí)例的隊(duì)列弊予。本小節(jié)我將給你演示如何使用這個 BlockingQueue祥楣。本節(jié)不會討論如何在 Java 中實(shí)現(xiàn)一個你自己的 BlockingQueue。如果你對那個感興趣汉柒,參考《Java 并發(fā)指南

BlockingQueue 用法

BlockingQueue 通常用于一個線程生產(chǎn)對象误褪,而另外一個線程消費(fèi)這些對象的場景。下圖是對這個原理的闡述:

一個線程往里邊放碾褂,另外一個線程從里邊取的一個 BlockingQueue兽间。

一個線程將會持續(xù)生產(chǎn)新對象并將其插入到隊(duì)列之中,直到隊(duì)列達(dá)到它所能容納的臨界點(diǎn)正塌。也就是說嘀略,它是有限的。如果該阻塞隊(duì)列到達(dá)了其臨界點(diǎn)传货,負(fù)責(zé)生產(chǎn)的線程將會在往里邊插入新對象時發(fā)生阻塞屎鳍。它會一直處于阻塞之中,直到負(fù)責(zé)消費(fèi)的線程從隊(duì)列中拿走一個對象问裕。負(fù)責(zé)消費(fèi)的線程將會一直從該阻塞隊(duì)列中拿出對象逮壁。如果消費(fèi)線程嘗試去從一個空的隊(duì)列中提取對象的話,這個消費(fèi)線程將會處于阻塞之中粮宛,直到一個生產(chǎn)線程把一個對象丟進(jìn)隊(duì)列窥淆。

BlockingQueue 的方法

BlockingQueue 具有 4 組不同的方法用于插入卖宠、移除以及對隊(duì)列中的元素進(jìn)行檢查。如果請求的操作不能得到立即執(zhí)行的話忧饭,每個方法的表現(xiàn)也不同扛伍。這些方法如下:

四組不同的行為方式解釋:

拋異常:如果試圖的操作無法立即執(zhí)行,拋一個異常词裤。

特定值:如果試圖的操作無法立即執(zhí)行刺洒,返回一個特定的值(常常是 true / false)。

阻塞:如果試圖的操作無法立即執(zhí)行吼砂,該方法調(diào)用將會發(fā)生阻塞逆航,直到能夠執(zhí)行。

超時:如果試圖的操作無法立即執(zhí)行渔肩,該方法調(diào)用將會發(fā)生阻塞因俐,直到能夠執(zhí)行,但等待時間不會超過給定值周偎。返回一個特定值以告知該操作是否成功(典型的是 true / false)抹剩。

無法向一個 BlockingQueue 中插入 null。如果你試圖插入 null蓉坎,BlockingQueue 將會拋出一個 NullPointerException澳眷。

可以訪問到 BlockingQueue 中的所有元素,而不僅僅是開始和結(jié)束的元素蛉艾。比如說境蔼,你將一個對象放入隊(duì)列之中以等待處理,但你的應(yīng)用想要將其取消掉伺通。那么你可以調(diào)用諸如 remove(o) 方法來將隊(duì)列之中的特定對象進(jìn)行移除箍土。但是這么干效率并不高(譯者注:基于隊(duì)列的數(shù)據(jù)結(jié)構(gòu),獲取除開始或結(jié)束位置的其他對象的效率不會太高)罐监,因此你盡量不要用這一類的方法吴藻,除非你確實(shí)不得不那么做。

BlockingQueue 的實(shí)現(xiàn)

BlockingQueue 是個接口弓柱,你需要使用它的實(shí)現(xiàn)之一來使用 BlockingQueue沟堡。java.util.concurrent 具有以下 BlockingQueue 接口的實(shí)現(xiàn)(Java 6):

ArrayBlockingQueue

DelayQueue

LinkedBlockingQueue

PriorityBlockingQueue

SynchronousQueue

Java 中使用 BlockingQueue 的例子

這里是一個 Java 中使用 BlockingQueue 的示例。本示例使用的是 BlockingQueue 接口的 ArrayBlockingQueue 實(shí)現(xiàn)矢空。

首先航罗,BlockingQueueExample 類分別在兩個獨(dú)立的線程中啟動了一個 Producer 和 一個 Consumer。

Producer 向一個共享的 BlockingQueue 中注入字符串屁药,而 Consumer 則會從中把它們拿出來粥血。

以下是 Producer 類。注意它在每次 put() 調(diào)用時是如何休眠一秒鐘的。這將導(dǎo)致 Consumer 在等待隊(duì)列中對象的時候發(fā)生阻塞复亏。


以下是 Consumer 類趾娃。它只是把對象從隊(duì)列中抽取出來,然后將它們打印到 System.out缔御。


3. 數(shù)組阻塞隊(duì)列 ArrayBlockingQueue

ArrayBlockingQueue 類實(shí)現(xiàn)了 BlockingQueue 接口抬闷。

ArrayBlockingQueue 是一個有界的阻塞隊(duì)列,其內(nèi)部實(shí)現(xiàn)是將對象放到一個數(shù)組里耕突。有界也就意味著笤成,它不能夠存儲無限多數(shù)量的元素。它有一個同一時間能夠存儲元素?cái)?shù)量的上限眷茁。你可以在對其初始化的時候設(shè)定這個上限疹启,但之后就無法對這個上限進(jìn)行修改了(譯者注:因?yàn)樗腔跀?shù)組實(shí)現(xiàn)的,也就具有數(shù)組的特性:一旦初始化蔼卡,大小就無法修改)。

‘ArrayBlockingQueue 內(nèi)部以 FIFO(先進(jìn)先出)的順序?qū)υ剡M(jìn)行存儲挣磨。隊(duì)列中的頭元素在所有元素之中是放入時間最久的那個雇逞,而尾元素則是最短的那個。

以下是在使用 ArrayBlockingQueue 的時候?qū)ζ涑跏蓟囊粋€示例:


4. 延遲隊(duì)列 DelayQueue

DelayQueue 實(shí)現(xiàn)了 BlockingQueue 接口茁裙。DelayQueue 對元素進(jìn)行持有直到一個特定的延遲到期塘砸。注入其中的元素必須實(shí)現(xiàn) java.util.concurrent.Delayed 接口,該接口定義:


DelayedElement 是我所創(chuàng)建的一個 DelayedElement 接口的實(shí)現(xiàn)類晤锥,它不在 Java.util.concurrent 包里掉蔬。你需要自行創(chuàng)建你自己的 Delayed 接口的實(shí)現(xiàn)以使用 DelayQueue 類。

5. 鏈阻塞隊(duì)列 LinkedBlockingQueue

LinkedBlockingQueue 類實(shí)現(xiàn)了 BlockingQueue 接口矾瘾。

LinkedBlockingQueue 內(nèi)部以一個鏈?zhǔn)浇Y(jié)構(gòu)(鏈接節(jié)點(diǎn))對其元素進(jìn)行存儲女轿。如果需要的話,這一鏈?zhǔn)浇Y(jié)構(gòu)可以選擇一個上限壕翩。如果沒有定義上限蛉迹,將使用 Integer.MAX_VALUE 作為上限。

LinkedBlockingQueue 內(nèi)部以 FIFO(先進(jìn)先出)的順序?qū)υ剡M(jìn)行存儲放妈。隊(duì)列中的頭元素在所有元素之中是放入時間最久的那個北救,而尾元素則是最短的那個。

以下是 LinkedBlockingQueue 的初始化和使用示例代碼:


6. 具有優(yōu)先級的阻塞隊(duì)列 PriorityBlockingQueue

PriorityBlockingQueue 類實(shí)現(xiàn)了 BlockingQueue 接口芜抒。

PriorityBlockingQueue 是一個無界的并發(fā)隊(duì)列珍策。它使用了和類 java.util.PriorityQueue 一樣的排序規(guī)則。你無法向這個隊(duì)列中插入 null 值宅倒。所有插入到 PriorityBlockingQueue 的元素必須實(shí)現(xiàn) java.lang.Comparable 接口攘宙。因此該隊(duì)列中元素的排序就取決于你自己的 Comparable 實(shí)現(xiàn)。注意 PriorityBlockingQueue 對于具有相等優(yōu)先級(compare() == 0)的元素并不強(qiáng)制任何特定行為。

同時注意模聋,如果你從一個 PriorityBlockingQueue 獲得一個 Iterator 的話肩民,該 Iterator 并不能保證它對元素的遍歷是以優(yōu)先級為序的。

以下是使用 PriorityBlockingQueue 的示例:


7. 同步隊(duì)列 SynchronousQueue

SynchronousQueue 類實(shí)現(xiàn)了 BlockingQueue 接口链方。

SynchronousQueue 是一個特殊的隊(duì)列持痰,它的內(nèi)部同時只能夠容納單個元素。如果該隊(duì)列已有一元素的話祟蚀,試圖向隊(duì)列中插入一個新元素的線程將會阻塞工窍,直到另一個線程將該元素從隊(duì)列中抽走。同樣前酿,如果該隊(duì)列為空患雏,試圖向隊(duì)列中抽取一個元素的線程將會阻塞,直到另一個線程向隊(duì)列中插入了一條新的元素罢维。

據(jù)此淹仑,把這個類稱作一個隊(duì)列顯然是夸大其詞了。它更多像是一個匯合點(diǎn)肺孵。

8. 阻塞雙端隊(duì)列 BlockingDeque

java.util.concurrent 包里的 BlockingDeque 接口表示一個線程安放入和提取實(shí)例的雙端隊(duì)列匀借。本小節(jié)我將給你演示如何使用 BlockingDeque。BlockingDeque 類是一個雙端隊(duì)列平窘,在不能夠插入元素時吓肋,它將阻塞住試圖插入元素的線程;在不能夠抽取元素時瑰艘,它將阻塞住試圖抽取的線程是鬼。deque(雙端隊(duì)列) 是 “Double Ended Queue” 的縮寫。因此紫新,雙端隊(duì)列是一個你可以從任意一端插入或者抽取元素的隊(duì)列均蜜。

BlockingDeque 的使用

在線程既是一個隊(duì)列的生產(chǎn)者又是這個隊(duì)列的消費(fèi)者的時候可以使用到 BlockingDeque。如果生產(chǎn)者線程需要在隊(duì)列的兩端都可以插入數(shù)據(jù)芒率,消費(fèi)者線程需要在隊(duì)列的兩端都可以移除數(shù)據(jù)兆龙,這個時候也可以使用 BlockingDeque。

一個 BlockingDeque – 線程在雙端隊(duì)列的兩端都可以插入和提取元素敲董。

一個線程生產(chǎn)元素紫皇,并把它們插入到隊(duì)列的任意一端。如果雙端隊(duì)列已滿腋寨,插入線程將被阻塞聪铺,直到一個移除線程從該隊(duì)列中移出了一個元素。如果雙端隊(duì)列為空萄窜,移除線程將被阻塞铃剔,直到一個插入線程向該隊(duì)列插入了一個新元素撒桨。

BlockingDeque 的方法

BlockingDeque 具有 4 組不同的方法用于插入、移除以及對雙端隊(duì)列中的元素進(jìn)行檢查键兜。如果請求的操作不能得到立即執(zhí)行的話凤类,每個方法的表現(xiàn)也不同普气。這些方法如下:

四組不同的行為方式解釋:

拋異常:如果試圖的操作無法立即執(zhí)行谜疤,拋一個異常。

特定值:如果試圖的操作無法立即執(zhí)行现诀,返回一個特定的值(常常是 true / false)夷磕。

阻塞:如果試圖的操作無法立即執(zhí)行,該方法調(diào)用將會發(fā)生阻塞仔沿,直到能夠執(zhí)行坐桩。

超時:如果試圖的操作無法立即執(zhí)行,該方法調(diào)用將會發(fā)生阻塞封锉,直到能夠執(zhí)行绵跷,但等待時間不會超過給定值。返回一個特定值以告知該操作是否成功(典型的是 true / false)成福。

BlockingDeque 繼承自 BlockingQueue

BlockingDeque 接口繼承自 BlockingQueue 接口碾局。

這就意味著你可以像使用一個 BlockingQueue 那樣使用 BlockingDeque。如果你這么干的話闷叉,各種插入方法將會把新元素添加到雙端隊(duì)列的尾端,而移除方法將會把雙端隊(duì)列的首端的元素移除脊阴。正如 BlockingQueue 接口的插入和移除方法一樣握侧。

以下是 BlockingDeque 對 BlockingQueue 接口的方法的具體內(nèi)部實(shí)現(xiàn):

BlockingDeque 的實(shí)現(xiàn)

既然 BlockingDeque 是一個接口,那么你想要使用它的話就得使用它的眾多的實(shí)現(xiàn)類的其中一個嘿期。java.util.concurrent 包提供了以下 BlockingDeque 接口的實(shí)現(xiàn)類:

LinkedBlockingDeque

BlockingDeque 代碼示例

以下是如何使用 BlockingDeque 方法的一個簡短代碼示例:


9. 鏈阻塞雙端隊(duì)列 LinkedBlockingDeque

LinkedBlockingDeque 類實(shí)現(xiàn)了 BlockingDeque 接口品擎。

deque(雙端隊(duì)列) 是 “Double Ended Queue” 的縮寫。因此备徐,雙端隊(duì)列是一個你可以從任意一端插入或者抽取元素的隊(duì)列萄传。(譯者注:唐僧啊,受不了蜜猾。)LinkedBlockingDeque 是一個雙端隊(duì)列秀菱,在它為空的時候,一個試圖從中抽取數(shù)據(jù)的線程將會阻塞蹭睡,無論該線程是試圖從哪一端抽取數(shù)據(jù)衍菱。以下是 LinkedBlockingDeque 實(shí)例化以及使用的示例:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肩豁,隨后出現(xiàn)的幾起案子脊串,更是在濱河造成了極大的恐慌辫呻,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琼锋,死亡現(xiàn)場離奇詭異放闺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缕坎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門怖侦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人念赶,你說我怎么就攤上這事础钠。” “怎么了叉谜?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵旗吁,是天一觀的道長。 經(jīng)常有香客問我停局,道長很钓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任董栽,我火速辦了婚禮码倦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锭碳。我一直安慰自己袁稽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布擒抛。 她就那樣靜靜地躺著推汽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歧沪。 梳的紋絲不亂的頭發(fā)上歹撒,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音诊胞,去河邊找鬼暖夭。 笑死,一個胖子當(dāng)著我的面吹牛撵孤,可吹牛的內(nèi)容都是我干的迈着。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼邪码,長吁一口氣:“原來是場噩夢啊……” “哼寥假!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霞扬,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤糕韧,失蹤者是張志新(化名)和其女友劉穎枫振,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萤彩,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粪滤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雀扶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杖小。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖愚墓,靈堂內(nèi)的尸體忽然破棺而出予权,到底是詐尸還是另有隱情,我是刑警寧澤浪册,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布扫腺,位于F島的核電站,受9級特大地震影響村象,放射性物質(zhì)發(fā)生泄漏笆环。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一厚者、第九天 我趴在偏房一處隱蔽的房頂上張望躁劣。 院中可真熱鬧,春花似錦库菲、人聲如沸账忘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖擒。三九已至,卻和暖如春奇颠,著一層夾襖步出監(jiān)牢的瞬間败去,已是汗流浹背放航。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工烈拒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人广鳍。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓荆几,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赊时。 傳聞我的和親對象是個殘疾皇子吨铸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

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