java 7 的 concurrenthashmap 通过分段锁(segment)实现线程安全:默认16个segment数组,每个segment是继承reentrantlock的小型哈希表,操作时仅锁定对应segment,支持最高16线程并发写;get无锁,依赖volatile保证可见性;扩容按segment独立进行。

Java 7 的 ConcurrentHashMap 通过分段锁(Segment)机制实现线程安全,核心思路是把大锁拆成多个小锁,让不同线程操作不同数据段时互不干扰。
分段锁的基本结构
它内部维护一个 Segment<k>[] segments</k> 数组,默认长度为 16。每个 Segment 继承自 ReentrantLock,本身又是一个小型哈希表(含自己的 HashEntry[] table 和链表结构)。线程操作前先定位到所属 Segment,再对该 Segment 加锁,而非锁住整个 Map。
- 哈希计算分两步:先用 key 的 hash 值对
segments.length取模,确定 Segment 下标;再对该 Segment 内部的table.length取模,定位具体桶位 - 默认并发级别为 16,即最多支持 16 个线程同时写入不同 Segment;可通过构造函数如
new ConcurrentHashMap(16, 0.75f, 32)指定并发级别(第三个参数)
put 操作如何触发分段加锁
调用 put(K key, V value) 时,流程如下:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 根据 key 的 hash 定位到某个 Segment 实例
- 调用该 Segment 的
put()方法——该方法是synchronized的(因为 Segment 是 ReentrantLock 子类,实际通过lock()获取独占锁) - 在锁定的 Segment 内部执行插入:检查是否需扩容、遍历链表判断 key 是否已存在、头插新节点等
这意味着:两个线程若 put 的 key 落在不同 Segment,完全并行;若落在同一 Segment,则第二个线程需等待锁释放。
get 操作为何不需要加锁
get(K key) 是无锁的,依赖 volatile 语义保证可见性:
- 同样先定位 Segment,再定位 HashEntry 链表头节点
- 所有 HashEntry 的
next字段和 value 字段都声明为volatile,确保读取时能拿到最新值 - 即使其他线程正在同一 Segment 中修改链表结构(如扩容、插入),get 仍可安全遍历——因为链表更新采用头插且字段 volatile,不会出现读到“半初始化”对象的情况
扩容是按 Segment 独立进行的
每个 Segment 维护自己的容量与阈值,扩容互不影响:
- 当某 Segment 内部元素数量超过
threshold = capacity × loadFactor时,仅该 Segment 触发扩容(新建更大数组,重哈希迁移) - 其他 Segment 照常读写,不受影响
- 这种局部扩容避免了全局 rehash 的长停顿,提升吞吐稳定性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










