Java JUC并發(fā)集合詳解之線程安全容器完全攻略
在多線程環(huán)境下,直接使用傳統(tǒng)的集合類(如ArrayList, HashMap) 是危險(xiǎn)的,會(huì)導(dǎo)致數(shù)據(jù)不一致、臟讀等問(wèn)題。Java通過(guò) java.util.concurrent(JUC)包提供了一整套線程安全的并發(fā)容器,它們不僅是簡(jiǎn)單的同步包裝,更是基于精妙并發(fā)算法構(gòu)建的高性能工具。理解并正確使用它們,是開(kāi)發(fā)穩(wěn)健、高效并發(fā)應(yīng)用的必備技能。
一、為什么需要JUC并發(fā)集合?
傳統(tǒng)的同步方式(如使用 Collections.synchronizedList())是通過(guò)在方法上添加synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)的,這是一種粗粒度的鎖策略,雖然能保證線程安全,但性能堪憂。因?yàn)樗淮沃辉试S一個(gè)線程訪問(wèn)集合,嚴(yán)重限制了并發(fā)性。
JUC并發(fā)容器的設(shè)計(jì)目標(biāo)是在保證線程安全的前提下,最大限度地提升并發(fā)性能。其核心實(shí)現(xiàn)理念是:
- 鎖分離:將鎖的粒度細(xì)化,允許多個(gè)線程以非沖突的方式同時(shí)訪問(wèn)集合的不同部分。
- 無(wú)鎖算法:使用CAS(Compare-And-Swap)等樂(lè)觀鎖操作,避免互斥鎖的開(kāi)銷。
- 寫時(shí)復(fù)制:適用于讀多寫少的場(chǎng)景,修改操作時(shí)復(fù)制整個(gè)容器,從而讓讀操作完全無(wú)鎖。
二、核心并發(fā)集合分類與詳解
JUC并發(fā)容器可分為以下幾大類,每一類都針對(duì)特定場(chǎng)景進(jìn)行了優(yōu)化。
1. 并發(fā)Map:應(yīng)對(duì)高頻鍵值存儲(chǔ)
ConcurrentHashMap(CHM) - 絕對(duì)的主角- 替代者:用于替代同步的
HashMap或Hashtable。 - 實(shí)現(xiàn)原理:在JDK 8之前,采用分段鎖(Segment),將數(shù)據(jù)分成一段一段存儲(chǔ),每段配一把鎖,大大提升了并發(fā)度。在JDK 8及之后,進(jìn)行了革命性優(yōu)化:
- 替代者:用于替代同步的
synchronized+ CAS:鎖的粒度從Segment細(xì)化到每個(gè)數(shù)組桶位的頭節(jié)點(diǎn),并發(fā)度更高。- 紅黑樹(shù)優(yōu)化:當(dāng)鏈表長(zhǎng)度超過(guò)8且數(shù)組容量≥64時(shí),鏈表會(huì)轉(zhuǎn)為紅黑樹(shù),防止極端情況下哈希沖突導(dǎo)致性能退化。
- 特點(diǎn):線程安全、高并發(fā)、高性能。是JUC中使用最頻繁的類之一。
ConcurrentSkipListMap- 特點(diǎn):基于跳表實(shí)現(xiàn)的有序并發(fā)Map。
- 適用場(chǎng)景:當(dāng)需要線程安全且要求Map的鍵有序時(shí)使用。其并發(fā)性能優(yōu)于同步的
TreeMap。

2. 并發(fā)Queue:高效的任務(wù)調(diào)度與數(shù)據(jù)傳輸
并發(fā)隊(duì)列是實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式的核心。

- 阻塞隊(duì)列 (BlockingQueue)
- 特點(diǎn):當(dāng)隊(duì)列滿時(shí),插入操作會(huì)被阻塞;當(dāng)隊(duì)列空時(shí),獲取操作會(huì)被阻塞。
- 核心實(shí)現(xiàn):
ArrayBlockingQueue:由數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列。內(nèi)部使用一個(gè)可重入鎖和兩個(gè)條件變量(notEmpty, notFull)來(lái)實(shí)現(xiàn)阻塞。LinkedBlockingQueue:由鏈表實(shí)現(xiàn)的可選有界阻塞隊(duì)列。默認(rèn)無(wú)界,但可設(shè)置容量。采用兩把鎖(putLock, takeLock),讀寫操作可以完全并行,吞吐量通常高于ArrayBlockingQueue。PriorityBlockingQueue:支持優(yōu)先級(jí)的無(wú)界阻塞隊(duì)列。SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等待另一個(gè)線程的對(duì)應(yīng)移除操作,反之亦然。非常適合直接傳遞任務(wù)的場(chǎng)景,吞吐量很高。
- 非阻塞隊(duì)列
ConcurrentLinkedQueue- 特點(diǎn):基于鏈表實(shí)現(xiàn)的無(wú)界非阻塞隊(duì)列。采用CAS算法實(shí)現(xiàn),保證了線程安全。
- 適用場(chǎng)景:適用于對(duì)性能要求極高,且可以容忍
poll()獲取元素時(shí)返回null(隊(duì)列為空)的場(chǎng)景。
3. 并發(fā)List與Set
CopyOnWriteArrayList(COW) - 寫時(shí)復(fù)制列表- 實(shí)現(xiàn)原理:所有修改操作(add, set, remove)都會(huì)復(fù)制底層數(shù)組。讀操作在原數(shù)組上進(jìn)行,因此讀操作極快且無(wú)需加鎖。修改操作完成后,將原數(shù)組引用指向新數(shù)組。
- 優(yōu)缺點(diǎn):
- 優(yōu)點(diǎn):讀性能極高,完全無(wú)鎖。
- 缺點(diǎn):內(nèi)存占用大(每次修改都復(fù)制整個(gè)數(shù)組);數(shù)據(jù)弱一致性(讀操作可能無(wú)法立即讀到剛寫入的數(shù)據(jù))。
- 適用場(chǎng)景:讀多寫少的極致場(chǎng)景,如監(jiān)聽(tīng)器列表、配置信息的存儲(chǔ)。
CopyOnWriteArraySet- 實(shí)現(xiàn)原理:內(nèi)部封裝了一個(gè)
CopyOnWriteArrayList,因此特性與COW列表完全一致。 - 適用場(chǎng)景:讀多寫少且需要集合語(yǔ)義的場(chǎng)景。
- 實(shí)現(xiàn)原理:內(nèi)部封裝了一個(gè)
4. 并發(fā)工具類(擴(kuò)展)
雖然不嚴(yán)格屬于集合,但常與并發(fā)集合配合使用。
ConcurrentSkipListSet:基于ConcurrentSkipListMap實(shí)現(xiàn)的有序并發(fā)Set。BlockingDeque:雙端阻塞隊(duì)列接口,代表實(shí)現(xiàn)是LinkedBlockingDeque。
三、選型指南:如何選擇合適的并發(fā)容器?
選擇正確的并發(fā)容器是優(yōu)化性能的關(guān)鍵。
| 場(chǎng)景 | 推薦容器 | 理由 |
|---|---|---|
| 高頻鍵值存取 | ConcurrentHashMap | 高并發(fā)、低延遲,全能選手 |
| 有序的鍵值存取 | ConcurrentSkipListMap | 線程安全且有序 |
| 生產(chǎn)者-消費(fèi)者模式 | ArrayBlockingQueue / LinkedBlockingQueue | 天然的阻塞特性,完美適配 |
| 直接任務(wù)傳遞 | SynchronousQueue | 高吞吐量的任務(wù)交接 |
| 極高的讀頻率,極少寫 | CopyOnWriteArrayList / CopyOnWriteArraySet | 讀操作無(wú)鎖,性能無(wú)敵 |
| 高性能非阻塞隊(duì)列 | ConcurrentLinkedQueue | CAS實(shí)現(xiàn),避免鎖開(kāi)銷 |
重要原則:
- 優(yōu)先使用JUC容器,而不是用
Collections.synchronizedXXX()來(lái)包裝舊容器。 - 明確需求:是需要Map、Queue還是List?是否需要阻塞特性?是否需要有序?
- 考慮讀寫比例:
CopyOnWrite系列只在寫極少的情況下有優(yōu)勢(shì)。 - 理解實(shí)現(xiàn)原理:了解底層是使用鎖還是CAS,有助于你判斷其在高競(jìng)爭(zhēng)下的性能表現(xiàn)。
到此這篇關(guān)于Java JUC并發(fā)集合詳解:線程安全容器完全指南的文章就介紹到這了,更多相關(guān)Java JUC并發(fā)集合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中Object toString方法簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Object類在Java里面是一個(gè)比較特殊的類,JAVA為了組織這個(gè)類組織得比較方便,它提供了一個(gè)最根上的類,相當(dāng)于所有的類都是從這個(gè)類繼承,這個(gè)類就叫Object。接下來(lái)通過(guò)本文給大家介紹Object toString方法,需要的的朋友參考下吧2017-05-05
詳解Spring batch 入門學(xué)習(xí)教程(附源碼)
本篇文章主要介紹了Spring batch 入門學(xué)習(xí)教程(附源碼),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Java中LocalCache本地緩存實(shí)現(xiàn)代碼
本篇文章主要介紹了Java中LocalCache本地緩存實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
volatile可見(jiàn)性的一些認(rèn)識(shí)和論證
volatile的關(guān)鍵詞的使用在JVM內(nèi)存模型中已是老生常談了,這篇文章主要結(jié)合自己對(duì)可見(jiàn)性的一些認(rèn)識(shí)和一些直觀的例子來(lái)談?wù)剉olatile,感興趣的朋友一起看看吧2017-08-08
springboot的jar能獨(dú)立運(yùn)行的原因解析
這篇文章主要介紹了springboot的jar能獨(dú)立運(yùn)行的原因解析,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
使用@PropertySource讀取配置文件通過(guò)@Value進(jìn)行參數(shù)注入
這篇文章主要介紹了使用@PropertySource讀取配置文件通過(guò)@Value進(jìn)行參數(shù)注入,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java編程實(shí)現(xiàn)二項(xiàng)分布的采樣或抽樣實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)二項(xiàng)分布的采樣或抽樣實(shí)例代碼,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01

