ConcurrentHashMap 如何秒殺 SynchronizedMap?
嗨,大家好,我是小米,一個(gè)熱愛分享技術(shù)的老碼農(nóng)(雖然不老,31歲正當(dāng)年!)。今天我們來聊聊 Java 面試中一道高頻問題:SynchronizedMap 和 ConcurrentHashMap 有什么區(qū)別?
這道題看似簡單,但其實(shí)暗藏玄機(jī),一不小心就容易掉坑。今天我們就通過一個(gè)故事 + 源碼剖析 + 性能對(duì)比,徹底搞清楚它們的區(qū)別。
故事開場:面試現(xiàn)場的靈魂拷問
某天,小王(5年Java開發(fā)經(jīng)驗(yàn))去參加阿里社招面試。
面試官看著簡歷,笑了笑:“你寫過高并發(fā)項(xiàng)目?”
小王點(diǎn)頭:“是的,我們系統(tǒng)日均 QPS 過百萬,主要用 HashMap 處理緩存?!?/p>
面試官挑了挑眉:“哦?那你能說說 SynchronizedMap 和 ConcurrentHashMap 的區(qū)別嗎?”
小王:“呃……它們都能保證線程安全……”
面試官:“嗯?那如果并發(fā)度很高,你會(huì)選哪個(gè)?”
小王開始緊張了:“這個(gè)……它們……嗯……”
面試官一笑:“好吧,我們換個(gè)問題……”
小王當(dāng)場淚目,回去后痛定思痛,決定徹底搞懂這兩個(gè)玩意兒!
那么,它們到底有什么區(qū)別呢?
SynchronizedMap vs ConcurrentHashMap 的核心區(qū)別
我們先來一個(gè)總結(jié)表格,直觀感受一下兩者的不同:
圖片
看完表格,我們一個(gè)個(gè)拆解講解。
SynchronizedMap:簡單粗暴的“全局鎖”
首先,SynchronizedMap 其實(shí)是 Collections.synchronizedMap() 生成的,它的本質(zhì)是對(duì) HashMap 進(jìn)行“全局加鎖”,保證每次訪問都只能有一個(gè)線程進(jìn)入。
我們看看源碼(簡化版):
圖片
內(nèi)部實(shí)現(xiàn)其實(shí)就是給 所有方法都加上 synchronized,讓所有操作串行化:
圖片
這樣雖然保證了線程安全,但也導(dǎo)致每次操作都要獲取整個(gè) Map 的鎖,性能相當(dāng)?shù)?,在高并發(fā)環(huán)境下會(huì)變成性能瓶頸!
適用場景
適用于并發(fā)較低,或者讀多寫少的場景,比如:
- 配置緩存
- 線程數(shù)不多的 Web 應(yīng)用
- 需要簡單線程安全的地方(例如同步讀取一小塊數(shù)據(jù))
如果是高并發(fā)環(huán)境,那就該換 ConcurrentHashMap 了!
ConcurrentHashMap:高并發(fā)神器
ConcurrentHashMap 是 Java 5 引入的,主要是為了提高并發(fā)性能。它的核心優(yōu)化點(diǎn)有兩個(gè):
- 分段鎖(Segment)(JDK 1.7 之前)
- CAS + 自旋鎖(JDK 1.8 之后)
讓我們看看 JDK 1.8 版本的 ConcurrentHashMap 怎么做到高性能的。
(1)底層結(jié)構(gòu)
在 JDK 1.8 之前,ConcurrentHashMap 采用 分段鎖(Segment),但 JDK 1.8 之后直接用 數(shù)組 + 鏈表 + 紅黑樹 代替了分段鎖,提高了效率。
- 存儲(chǔ)結(jié)構(gòu):類似 HashMap,但每個(gè)桶(bucket)都是 鏈表 + 紅黑樹,高并發(fā)時(shí)可變成紅黑樹加速查詢。
- 鎖機(jī)制:不鎖整個(gè) Map,而是只鎖住某個(gè) bucket,這樣多個(gè)線程可以同時(shí)訪問不同的 bucket,提高并發(fā)能力。
- CAS(Compare And Swap):無鎖操作,提升性能。
(2)put() 方法解析
看源碼(簡化版):
圖片
可以看到,它不會(huì)鎖整個(gè) Map,而是:
- 先用 CAS 嘗試放入數(shù)據(jù),避免不必要的鎖競爭。
- 如果 CAS 失敗,再只鎖住當(dāng)前 bucket,而不是整個(gè) Map,提高了并發(fā)性能。
適用場景
- 高并發(fā)環(huán)境(推薦)
- 大數(shù)據(jù)量存儲(chǔ)
- 讀寫并重的場景
- 緩存(如 LRU Cache)
性能對(duì)比實(shí)驗(yàn)
我們做個(gè)小實(shí)驗(yàn),對(duì)比兩者的性能(JMH 基準(zhǔn)測(cè)試):
圖片
實(shí)驗(yàn)結(jié)果(吞吐量 ops/sec):
圖片
可以看到,并發(fā)越高,SynchronizedMap 退化得越厲害,而 ConcurrentHashMap 能保持高性能。
總結(jié):面試高分回答
如果你在面試中遇到這個(gè)問題,你可以這樣答:
“SynchronizedMap 是對(duì)整個(gè) HashMap 加鎖,適用于低并發(fā)場景。而 ConcurrentHashMap 通過 分段鎖 + CAS 提高并發(fā)性能,在高并發(fā)環(huán)境下比 SynchronizedMap 更優(yōu)。并且,SynchronizedMap 允許 null 鍵值,而 ConcurrentHashMap 不允許。”
這下,面試官該對(duì)你點(diǎn)頭了吧?






