Java 8 ConcurrentHashMap 桶級別鎖實現機制
Java 8 中的 ConcurrentHashMap 拋棄了分段鎖設計,采用了更細粒度的桶級別鎖(bucket-level locking)實現,這是其并發性能提升的關鍵。下面詳細解析其實現原理:
1. 基本實現原理
桶級別鎖的核心思想是:只鎖定當前操作的哈希桶(鏈表或樹的頭節點),而不是整個表或某個段。
關鍵實現方式:
- CAS + synchronized 組合控制
- 鎖對象是每個桶的頭節點
- 讀操作完全無鎖(volatile讀)
- 寫操作僅在必要時加鎖
2. 具體實現細節
2.1 鎖獲取方式
// putVal方法中的鎖獲取片段
synchronized (f) { // f是桶的頭節點if (tabAt(tab, i) == f) { // 雙重檢查// 執行插入操作...}
}
2.2 鎖的獲取條件
- 桶不為空時才會加鎖
- 只鎖住當前桶的頭節點
- 通過
synchronized
關鍵字實現(JVM優化后的偏向鎖/輕量級鎖)
3. 實現步驟分解
3.1 插入元素時的鎖控制流程
- 計算 key 的哈希并定位桶位置
- 如果桶為空,使用 CAS 無鎖插入
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))break;
- 如果桶不為空,則鎖住頭節點:
synchronized (f) {// 處理鏈表或樹 }
3.2 鎖的細粒度驗證
synchronized (f) {if (tabAt(tab, i) == f) { // 再次確認當前節點仍是頭節點// 實際處理邏輯}
}
這種雙重檢查確保:
- 防止在獲取鎖期間桶已被其他線程修改
- 保證操作的原子性
4. 技術優勢
-
更高的并發度:
- 理論上并發度 = 哈希桶數量(默認16 → 可能成千上萬)
-
更低的鎖競爭:
- 不同桶的操作完全并行
- 只有哈希沖突時才會競爭
-
自適應鎖優化:
- JVM 會根據競爭情況自動升級鎖(偏向鎖→輕量級鎖→重量級鎖)
-
與擴容協同:
- 擴容時通過 ForwardingNode 保證操作可以繼續
5. 與 Java 7 分段鎖對比
特性 | Java 7 分段鎖 | Java 8 桶級別鎖 |
---|---|---|
鎖粒度 | 段(默認16) | 單個桶 |
鎖實現 | ReentrantLock | synchronized |
讀操作 | 需要段鎖 | 完全無鎖 |
擴容 | 段獨立擴容 | 整體協同擴容 |
沖突處理 | 鏈表 | 鏈表+紅黑樹 |
6. 實際代碼示例
6.1 鏈表情況下的鎖應用
// putVal方法片段
synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) { // 普通鏈表節點binCount = 1;for (Node<K,V> e = f;; ++binCount) {// 遍歷鏈表...if ((e = e.next) == null) {e.next = new Node<K,V>(hash, key, value, null);break;}}}}
}
6.2 樹情況下的鎖應用
else if (f instanceof TreeBin) {Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}
}
7. 關鍵設計考量
-
synchronized 優化:
- Java 6 后 synchronized 性能已大幅提升
- JVM 會根據競爭情況自動優化
-
CAS 失敗回退:
- 當 CAS 插入失敗(競爭)時才使用鎖
-
死鎖避免:
- 只按固定順序獲取單個鎖
- 不會出現交叉鎖請求
-
內存可見性:
- 通過 volatile 變量保證
- setTabAt/tabAt 使用 Unsafe 保證原子性
這種桶級別鎖設計使得 ConcurrentHashMap 在 Java 8 中實現了:
- 更高的并發吞吐量
- 更低的操作延遲
- 更平滑的性能曲線
- 更好的可擴展性