java.util.concurrent包下的并發(fā)容器

一骤坐、簡(jiǎn)述

不考慮多線程并發(fā)的情況下,容器類(lèi)一般使用ArrayList下愈、HashMap等線程不安全的類(lèi)纽绍,效率更高。并發(fā)場(chǎng)景势似,常用到ConcurrentHashMap拌夏、ArrayBlockingQueue等線程安全的容器類(lèi),雖然犧牲了一些效率叫编,但卻得到了安全辖佣。

二霹抛、java.util.concurrent包下的線程安全容器

  1. ConcurrentHashMap:并發(fā)版HashMap
  2. CopyOnWriteArrayList:并發(fā)版ArrayList
  3. CopyOnWriteArraySet:并發(fā)Set
  4. ConcurrentLinkedQueue:并發(fā)隊(duì)列(基于鏈表)
  5. ConcurrentLinkedDeque:并發(fā)隊(duì)列(基于雙向鏈表)
  6. ConcurrentSkipListMap:基于跳表的并發(fā)Map
  7. ConcurrentSkipListSet:基于跳表的并發(fā)Set
  8. ArrayBlockingQueue:阻塞隊(duì)列(基于數(shù)組)
  9. LinkedBlockingQueue:阻塞隊(duì)列(基于鏈表)
  10. LinkedBlockingDeque:阻塞隊(duì)列(基于雙向鏈表)
  11. PriorityBlockingQueue:線程安全的優(yōu)先隊(duì)列
  12. SynchronousQueue:讀寫(xiě)成對(duì)的隊(duì)列
  13. LinkedTransferQueue:基于鏈表的數(shù)據(jù)交換隊(duì)列
  14. DelayQueue:延時(shí)隊(duì)列

1?? ConcurrentHashMap并發(fā)版HashMap

最常見(jiàn)的并發(fā)容器之一搓逾,可以用作并發(fā)場(chǎng)景下的緩存。底層依然是哈希表杯拐,但在JAVA 8中有了不小的改變霞篡,而JAVA 7和JAVA 8都是用的比較多的版本,面試中經(jīng)常會(huì)將這兩個(gè)版本的實(shí)現(xiàn)方式做一些比較端逼。

一個(gè)比較大的差異就是朗兵,JAVA 7中采用分段鎖來(lái)減少鎖的競(jìng)爭(zhēng),JAVA 8中放棄了分段鎖顶滩,采用CAS余掖,同時(shí)為了防止哈希沖突嚴(yán)重時(shí)退化成鏈表(沖突時(shí)會(huì)在該位置生成一個(gè)鏈表,哈希值相同的對(duì)象就鏈在一起)礁鲁,會(huì)在鏈表長(zhǎng)度達(dá)到閾值(8)后轉(zhuǎn)換成紅黑樹(shù)(比起鏈表盐欺,樹(shù)的查詢(xún)效率更穩(wěn)定)赁豆。

2??CopyOnWriteArrayList 并發(fā)版ArrayList

并發(fā)版ArrayList,底層結(jié)構(gòu)也是數(shù)組冗美,和ArrayList不同之處在于:當(dāng)新增和刪除元素時(shí)會(huì)創(chuàng)建一個(gè)新的數(shù)組魔种,在新的數(shù)組中增加或者排除指定對(duì)象,最后用新增數(shù)組替換原來(lái)的數(shù)組粉洼。

適用場(chǎng)景:由于讀操作不加鎖节预,寫(xiě)(增、刪属韧、改)操作加鎖安拟,因此適用于讀多寫(xiě)少的場(chǎng)景。

局限:由于讀的時(shí)候不會(huì)加鎖(讀的效率高,就和普通ArrayList一樣)乎婿,讀取的當(dāng)前副本怪与,因此可能讀取到臟數(shù)據(jù)。如果介意愉棱,建議不用。

看看源碼感受下:

3??CopyOnWriteArraySet 并發(fā)Set

基于CopyOnWriteArrayList實(shí)現(xiàn)(內(nèi)含一個(gè)CopyOnWriteArrayList成員變量)哲戚,也就是說(shuō)底層是一個(gè)數(shù)組奔滑,意味著每次add都要遍歷整個(gè)集合才能知道是否存在,不存在時(shí)需要插入(加鎖)顺少。

適用場(chǎng)景:在CopyOnWriteArrayList適用場(chǎng)景下加一個(gè)朋其,集合別太大(全部遍歷傷不起)。

4??ConcurrentLinkedQueue 并發(fā)隊(duì)列(基于鏈表)

基于鏈表實(shí)現(xiàn)的并發(fā)隊(duì)列脆炎,使用樂(lè)觀鎖(CAS)保證線程安全梅猿。因?yàn)閿?shù)據(jù)結(jié)構(gòu)是鏈表,所以理論上是沒(méi)有隊(duì)列大小限制的秒裕,也就是說(shuō)添加數(shù)據(jù)一定能成功袱蚓。

5??ConcurrentLinkedDeque 并發(fā)隊(duì)列(基于雙向鏈表)

基于雙向鏈表實(shí)現(xiàn)的并發(fā)隊(duì)列,可以分別對(duì)頭尾進(jìn)行操作几蜻,因此除了先進(jìn)先出(FIFO)喇潘,也可以先進(jìn)后出(FILO),當(dāng)然先進(jìn)后出的話應(yīng)該叫它棧了梭稚。

6??ConcurrentSkipListMap 基于跳表的并發(fā)Map

SkipList即跳表颖低,跳表是一種空間換時(shí)間的數(shù)據(jù)結(jié)構(gòu),通過(guò)冗余數(shù)據(jù)弧烤,將鏈表一層一層索引忱屑,達(dá)到類(lèi)似二分查找的效果

7??ConcurrentSkipListSet 基于跳表的并發(fā)Set

類(lèi)似HashSet和HashMap的關(guān)系,ConcurrentSkipListSet里面就是一個(gè)ConcurrentSkipListMap,就不細(xì)說(shuō)了莺戒。

8??ArrayBlockingQueue 阻塞隊(duì)列(基于數(shù)組)

基于數(shù)組實(shí)現(xiàn)的可阻塞隊(duì)列粱栖,構(gòu)造時(shí)必須制定數(shù)組大小,往里面放東西時(shí)如果數(shù)組滿了便會(huì)阻塞直到有位置(也支持直接返回和超時(shí)等待)脏毯,通過(guò)一個(gè)鎖ReentrantLock保證線程安全闹究。

乍一看會(huì)有點(diǎn)疑惑,讀和寫(xiě)都是同一個(gè)鎖食店,那要是空的時(shí)候正好一個(gè)讀線程來(lái)了不會(huì)一直阻塞嗎渣淤?

答案就在notEmpty、notFull里吉嫩,這兩個(gè)出自lock的小東西讓鎖有了類(lèi)似synchronized + wait + notify的功能价认。傳送門(mén) → 終于搞懂了sleep/wait/notify/notifyAll

9??LinkedBlockingQueue 阻塞隊(duì)列(基于鏈表)
基于鏈表實(shí)現(xiàn)的阻塞隊(duì)列,想比與不阻塞的ConcurrentLinkedQueue自娩,它多了一個(gè)容量限制用踩,如果不設(shè)置默認(rèn)為int最大值。

1??0??LinkedBlockingDeque 阻塞隊(duì)列(基于雙向鏈表)

類(lèi)似LinkedBlockingQueue忙迁,但提供了雙向鏈表特有的操作脐彩。

1??1??PriorityBlockingQueue 線程安全的優(yōu)先隊(duì)列

構(gòu)造時(shí)可以傳入一個(gè)比較器,可以看做放進(jìn)去的元素會(huì)被排序姊扔,然后讀取的時(shí)候按順序消費(fèi)惠奸。某些低優(yōu)先級(jí)的元素可能長(zhǎng)期無(wú)法被消費(fèi),因?yàn)椴粩嘤懈邇?yōu)先級(jí)的元素進(jìn)來(lái)恰梢。

1??2??SynchronousQueue 數(shù)據(jù)同步交換的隊(duì)列

一個(gè)虛假的隊(duì)列佛南,因?yàn)樗鼘?shí)際上沒(méi)有真正用于存儲(chǔ)元素的空間,每個(gè)插入操作都必須有對(duì)應(yīng)的取出操作嵌言,沒(méi)取出時(shí)無(wú)法繼續(xù)放入嗅回。

import java.util.concurrent.SynchronousQueue;
public class Main {
    
    public static void main(String[] args) {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>();
        new Thread(()->{
            try{
                for(int i=0;;i++){
                    System.out.println("放入:" + i);
                    queue.put(i);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try{
                while(true){
                    System.out.println("取出:" + queue.take());
                    Thread.sleep((long)(Math.random()*2000));
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }).start();
    }
}

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

取出:0
放入:0
取出:1
放入:1
放入:2
取出:2
取出:3
放入:3
取出:4
放入:4
...
...

可以看到,寫(xiě)入的線程沒(méi)有任何sleep摧茴,可以說(shuō)是全力往隊(duì)列放東西绵载,而讀取的線程又很不積極,讀一個(gè)又sleep一會(huì)蓬蝶。輸出的結(jié)果卻是讀寫(xiě)操作成對(duì)出現(xiàn)尘分。

JAVA中一個(gè)使用場(chǎng)景就是Executors.newCachedThreadPool(),創(chuàng)建一個(gè)緩存線程池丸氛。

1??3??LinkedTransferQueue 基于鏈表的數(shù)據(jù)交換隊(duì)列

實(shí)現(xiàn)了接口TransferQueue,通過(guò)transfer方法放入元素時(shí)著摔,如果發(fā)現(xiàn)有線程在阻塞在取元素缓窜,會(huì)直接把這個(gè)元素給等待線程。如果沒(méi)有人等著消費(fèi),那么會(huì)把這個(gè)元素放到隊(duì)列尾部禾锤,并且此方法阻塞直到有人讀取這個(gè)元素私股。和SynchronousQueue有點(diǎn)像,但比它更強(qiáng)大恩掷。

1??4??DelayQueue 延時(shí)隊(duì)列

可以使放入隊(duì)列的元素在指定的延時(shí)后才被消費(fèi)者取出倡鲸,元素需要實(shí)現(xiàn)Delayed接口。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末黄娘,一起剝皮案震驚了整個(gè)濱河市峭状,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逼争,老刑警劉巖优床,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異誓焦,居然都是意外死亡胆敞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)杂伟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)移层,“玉大人,你說(shuō)我怎么就攤上這事赫粥∮母郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵傅是,是天一觀的道長(zhǎng)匪燕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)喧笔,這世上最難降的妖魔是什么帽驯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮书闸,結(jié)果婚禮上尼变,老公的妹妹穿的比我還像新娘。我一直安慰自己浆劲,他們只是感情好嫌术,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著牌借,像睡著了一般度气。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上膨报,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天磷籍,我揣著相機(jī)與錄音适荣,去河邊找鬼。 笑死院领,一個(gè)胖子當(dāng)著我的面吹牛弛矛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播比然,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼丈氓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了强法?” 一聲冷哼從身側(cè)響起万俗,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拟烫,沒(méi)想到半個(gè)月后该编,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硕淑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年课竣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片置媳。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡于樟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拇囊,到底是詐尸還是另有隱情迂曲,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布寥袭,位于F島的核電站路捧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏传黄。R本人自食惡果不足惜杰扫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膘掰。 院中可真熱鬧章姓,春花似錦、人聲如沸识埋。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窒舟。三九已至系忙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辜纲,已是汗流浹背笨觅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工拦耐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耕腾,地道東北人见剩。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像扫俺,于是被迫代替她去往敵國(guó)和親苍苞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359