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

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

- 阻塞隊列 (BlockingQueue)
- 特點:當隊列滿時,插入操作會被阻塞;當隊列空時,獲取操作會被阻塞。
- 核心實現(xiàn):
ArrayBlockingQueue:由數(shù)組實現(xiàn)的有界阻塞隊列。內(nèi)部使用一個可重入鎖和兩個條件變量(notEmpty, notFull)來實現(xiàn)阻塞。LinkedBlockingQueue:由鏈表實現(xiàn)的可選有界阻塞隊列。默認無界,但可設置容量。采用兩把鎖(putLock, takeLock),讀寫操作可以完全并行,吞吐量通常高于ArrayBlockingQueue。PriorityBlockingQueue:支持優(yōu)先級的無界阻塞隊列。SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等待另一個線程的對應移除操作,反之亦然。非常適合直接傳遞任務的場景,吞吐量很高。
- 非阻塞隊列
ConcurrentLinkedQueue- 特點:基于鏈表實現(xiàn)的無界非阻塞隊列。采用CAS算法實現(xiàn),保證了線程安全。
- 適用場景:適用于對性能要求極高,且可以容忍
poll()獲取元素時返回null(隊列為空)的場景。
3. 并發(fā)List與Set
CopyOnWriteArrayList(COW) - 寫時復制列表- 實現(xiàn)原理:所有修改操作(add, set, remove)都會復制底層數(shù)組。讀操作在原數(shù)組上進行,因此讀操作極快且無需加鎖。修改操作完成后,將原數(shù)組引用指向新數(shù)組。
- 優(yōu)缺點:
- 優(yōu)點:讀性能極高,完全無鎖。
- 缺點:內(nèi)存占用大(每次修改都復制整個數(shù)組);數(shù)據(jù)弱一致性(讀操作可能無法立即讀到剛寫入的數(shù)據(jù))。
- 適用場景:讀多寫少的極致場景,如監(jiān)聽器列表、配置信息的存儲。
CopyOnWriteArraySet- 實現(xiàn)原理:內(nèi)部封裝了一個
CopyOnWriteArrayList,因此特性與COW列表完全一致。 - 適用場景:讀多寫少且需要集合語義的場景。
- 實現(xiàn)原理:內(nèi)部封裝了一個
4. 并發(fā)工具類(擴展)
雖然不嚴格屬于集合,但常與并發(fā)集合配合使用。
ConcurrentSkipListSet:基于ConcurrentSkipListMap實現(xiàn)的有序并發(fā)Set。BlockingDeque:雙端阻塞隊列接口,代表實現(xiàn)是LinkedBlockingDeque。
三、選型指南:如何選擇合適的并發(fā)容器?
選擇正確的并發(fā)容器是優(yōu)化性能的關(guān)鍵。
| 場景 | 推薦容器 | 理由 |
|---|---|---|
| 高頻鍵值存取 | ConcurrentHashMap | 高并發(fā)、低延遲,全能選手 |
| 有序的鍵值存取 | ConcurrentSkipListMap | 線程安全且有序 |
| 生產(chǎn)者-消費者模式 | ArrayBlockingQueue / LinkedBlockingQueue | 天然的阻塞特性,完美適配 |
| 直接任務傳遞 | SynchronousQueue | 高吞吐量的任務交接 |
| 極高的讀頻率,極少寫 | CopyOnWriteArrayList / CopyOnWriteArraySet | 讀操作無鎖,性能無敵 |
| 高性能非阻塞隊列 | ConcurrentLinkedQueue | CAS實現(xiàn),避免鎖開銷 |
重要原則:
- 優(yōu)先使用JUC容器,而不是用
Collections.synchronizedXXX()來包裝舊容器。 - 明確需求:是需要Map、Queue還是List?是否需要阻塞特性?是否需要有序?
- 考慮讀寫比例:
CopyOnWrite系列只在寫極少的情況下有優(yōu)勢。 - 理解實現(xiàn)原理:了解底層是使用鎖還是CAS,有助于你判斷其在高競爭下的性能表現(xiàn)。
到此這篇關(guān)于Java JUC并發(fā)集合詳解:線程安全容器完全指南的文章就介紹到這了,更多相關(guān)Java JUC并發(fā)集合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MybatisPlus創(chuàng)建時間不想用默認值的問題
MybatisPlus通過FieldFill注解和MpMetaObjectHandler類支持自動填充字段功能,特別地,可以設置字段在插入或更新時自動填充創(chuàng)建時間和更新時間,但在特定場景下,如導入數(shù)據(jù)時,可能需要自定義創(chuàng)建時間2024-09-09
spring boot 自定義規(guī)則訪問獲取內(nèi)部或者外部靜態(tài)資源圖片的方法
這篇文章主要介紹了spring boot 自定義規(guī)則訪問獲取內(nèi)部或者外部靜態(tài)資源圖片的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
Springboot 讀取自定義pro文件注入static靜態(tài)變量方式
這篇文章主要介紹了Springboot 讀取自定義pro文件注入static靜態(tài)變量方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
maven升級版本后報錯:Blocked mirror for repositories
本文主要介紹了maven升級版本后報錯:Blocked mirror for repositories,文中的解決方法非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-09-09
關(guān)于@RequestBody和@RequestParam注解的使用詳解
這篇文章主要介紹了關(guān)于@RequestBody和@RequestParam注解的使用詳解,本文十分具有參考意義,希望可以幫助到你,如果有錯誤的地方還望不吝賜教2023-03-03

