Skip to content

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 时:

  1. 根据 key 的 hash 值找到它属于哪个 segment;
  2. 只锁住那个 segment;
  3. 其他 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 是:

  1. 计算 key 的 hash;
  2. 定位到对应的 Segment;
  3. 获取该 Segment 的锁;
  4. 修改该 Segment 内部的链表或桶;
  5. 释放锁。

⚡ 因此多个线程只要操作的是不同的 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 操作。

随便写写的,喜欢就好。 使用VitePress构建