鎖分段是一種降低鎖競爭、提高並發吞吐量的經典技術。核心思想:將資料結構分成多個段,每段獨立加鎖,不同段操作可並行執行。Java ConcurrentHashMap 即採用此設計。
public class StripedMap {
private static final int N_LOCKS = 16; // 鎖條帶數量,通常為 2 的冪次
private final Node[] buckets; // 雜湊桶陣列
private final Object[] locks; // 鎖陣列,每個鎖守護一組桶
private static class Node {
String key, value;
Node next; // 鏈表節點
}
}
public StripedMap(int numBuckets) {
buckets = new Node[numBuckets];
locks = new Object[N_LOCKS];
for (int i = 0; i < N_LOCKS; i++) {
locks[i] = new Object(); // 每個鎖獨立物件
}
}
private final int hash(Object key) {
return Math.abs(key.hashCode() % buckets.length);
}
// 關鍵:hash % N_LOCKS 決定使用哪把鎖
// 同一個 key 永遠映射到同一把鎖,保證線程安全
public Object get(Object key) {
int hash = hash(key);
// 只鎖住對應的段
synchronized (locks[hash % N_LOCKS]) {
for (Node m = buckets[hash]; m != null; m = m.next) {
if (m.key.equals(key)) return m.value;
}
}
return null;
}
public void clear() {
for (int i = 0; i < buckets.length; i++) {
// 逐段加鎖清理,而非一次鎖住全部
synchronized (locks[i % N_LOCKS]) {
buckets[i] = null;
}
}
}
| 特性 | StripedMap | ConcurrentHashMap (JDK7) |
|---|---|---|
| Segment 數量 | 固定 16 (N_LOCKS) | 預設 16 (concurrencyLevel) |
| 鎖機制 | Object 陣列 + synchronized | ReentrantLock + Segment 繼承 |
| 擴容支援 | 無 | 支援動態擴容 |
| 適用場景 | 教學、簡易快取 | 生產級高並發 Map |
JDK 8 ConcurrentHashMap 採用 CAS + synchronized + 紅黑樹,進一步降低鎖粒度到桶級別,並解決鏈表過長退化為 O(n) 查詢問題。
StripedMap 以簡潔的程式碼展示了鎖分段的核心精髓:將單一鎖拆分為多把鎖,根據雜湊值分散鎖競爭。這是理解 ConcurrentHashMap、LongAdder、ThreadLocal 等高並發組件設計思想的基石。實際生產環境建議直接使用 JDK 提供的 ConcurrentHashMap、ConcurrentSkipListMap 等經過嚴格測試的並發集合。