Java 分段锁 (Segmented Lock)
🧩 一、问题背景:为什么需要“分段锁”
在多线程环境下,如果你要让多个线程安全地访问一个共享的 Map(例如 HashMap
),最直接的做法是:
java
Map<K, V> map = Collections.synchronizedMap(new HashMap<>());
这种做法会在整个 Map 上加一把锁。 → 结果:任何线程只要访问 Map,都得等锁释放。
这就造成严重的性能瓶颈,比如:
- 线程 A 在 put(key1, value1)
- 线程 B 在 get(key2) 即使两者访问的是不同 key,也得互相等待。😩
于是,人们提出了“分段锁”的思想。
⚙️ 二、分段锁的核心思想
核心概念一句话:
把一个大锁拆分成多把小锁,每一小段(segment)管理一部分数据。
想象一下:
ConcurrentHashMap
├── Segment[0] —— 管理部分桶(bucket)
├── Segment[1] —— 管理部分桶(bucket)
├── Segment[2] —— 管理部分桶(bucket)
└── ...
每个 Segment
都有自己的锁。 当线程访问一个 key 时:
- 根据 key 的 hash 值找到它属于哪个 segment;
- 只锁住那个 segment;
- 其他 segment 不受影响。
👉 并发度大大提升。
🧠 三、在 Java 里的实现(以 JDK 1.7 为例)
在 JDK 1.7 的 ConcurrentHashMap
中:
- 内部有一个
Segment<K,V>[] segments
数组。 - 每个
Segment
都继承自ReentrantLock
。 - 每个
Segment
内部再维护一个小的HashEntry
数组。
示意图如下:
ConcurrentHashMap
├── Segment[0] → Lock A → HashEntry[] A
├── Segment[1] → Lock B → HashEntry[] B
├── Segment[2] → Lock C → HashEntry[] C
└── ...
当我们执行:
java
map.put("key", value);
执行流程 roughly 是:
- 计算 key 的 hash;
- 定位到对应的 Segment;
- 获取该 Segment 的锁;
- 修改该 Segment 内部的链表或桶;
- 释放锁。
⚡ 因此多个线程只要操作的是不同的 segment,就可以并行执行。
🚀 四、JDK 1.8 后的变化
在 JDK 1.8 之后,ConcurrentHashMap
移除了 Segment 结构,改用更细粒度的Node + CAS + synchronized机制。
也就是说:
- 不再显式分段;
- 使用
synchronized
锁在单个链表或树节点上; - 通过
CAS
(Compare-And-Swap)减少锁竞争。
但理念上仍然是“分段思想的延伸”:
锁的粒度更细,只锁必要的局部。
📊 五、总结类比记忆
方案 | 锁粒度 | 特点 | 举例 |
---|---|---|---|
HashMap + synchronized | 整体锁 | 简单但慢 | 一次只能一个人进 |
ConcurrentHashMap (JDK 1.7) | Segment锁 | 分段并行 | 超市多收银台 |
ConcurrentHashMap (JDK 1.8+) | Node级锁 + CAS | 更细粒度 | 每个货架单独锁 |
🧩 六、简单示例代码(理解用)
java
public class SegmentLockDemo {
private final int segmentCount = 16;
private final Object[] locks = new Object[segmentCount];
private final Map<Integer, String>[] maps = new Map[segmentCount];
public SegmentLockDemo() {
for (int i = 0; i < segmentCount; i++) {
locks[i] = new Object();
maps[i] = new HashMap<>();
}
}
private int getSegmentIndex(Object key) {
return key.hashCode() & (segmentCount - 1);
}
public void put(Integer key, String value) {
int index = getSegmentIndex(key);
synchronized (locks[index]) {
maps[index].put(key, value);
}
}
public String get(Integer key) {
int index = getSegmentIndex(key);
synchronized (locks[index]) {
return maps[index].get(key);
}
}
}
这段代码模拟了分段锁的基本思路: 不同的 key 分配到不同的锁上,从而实现并发。
✅ 七、总结一句话记忆:
分段锁是一种“以空间换时间”的并发策略, 通过将整体锁拆分成多个独立锁来提升并发性能。
在 JDK 1.7 中是显式的 Segment 实现, 在 JDK 1.8+ 中演化为更细粒度的 Node 锁和 CAS 操作。