concurrenthashmap在jdk 7中采用分段锁(segment)机制,即把哈希表划分为默认16个独立加锁的segment,每个segment继承reentrantlock并维护自己的hashentry数组和链表,实现多线程对不同段的并发操作。

在 JDK 7 中,ConcurrentHashMap 通过分段锁(Segment)机制实现高并发下的线程安全,核心思想是**把整个哈希表切分成多个独立加锁的段(Segment),每个 Segment 相当于一个小型的 Hashtable,各自维护自己的锁和数据**。这样,不同线程操作不同段时互不阻塞,显著提升并发性能。
Segment 是什么?
Segment 继承自 ReentrantLock,既是锁,也是数据容器。每个 Segment 内部持有:
- 一个
HashEntry[] table数组(类似 HashMap 的桶数组) - 自己的 volatile count、modCount 等状态字段
- 独立的 reentrant lock(可重入锁)
默认构造下,ConcurrentHashMap 初始化 16 个 Segment(即并发级别 concurrencyLevel = 16),意味着最多支持 16 个线程同时写入不同段而无竞争。
如何定位 key 属于哪个 Segment?
不是直接用 key.hashCode() 对 segment 数组长度取模,而是:
- 先对 key 的 hash 值进行二次散列(
hash = HashEntry.hash(key.hashCode())),增强低位随机性 - 再用高位 bits 参与寻址:
int j = (hash >>> segmentShift) & segmentMask -
segmentShift是使2^segmentShift == segments.length的位移量(如 16 段 → shift=4) -
segmentMask = segments.length - 1,用于快速取模
例如:segments 长度为 16(0b10000),则 segmentShift = 4,segmentMask = 15(0b1111)。hash 值右移 4 位后与 15 与运算,得到 0~15 的索引。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
put 操作怎么加锁和写入?
以 put(K key, V value) 为例:
- 先根据 key 定位到具体 Segment(如 seg = segments[j])
- 调用
seg.put(key, hash, value, false)—— 这里会尝试获取该 Segment 的独占锁(可能阻塞) - 加锁成功后,在该 Segment 内部按 HashEntry 链表方式插入(JDK 7 中无红黑树,只有链表)
- 插入前会检查是否需要扩容:若当前容量超过 threshold 且 table 不为空,则对该 Segment 单独扩容(不影响其他 Segment)
注意:get 操作全程无锁,依靠 volatile 变量(如 HashEntry.next、value)的 happens-before 语义保证可见性。
为什么说它比 Hashtable 更高效?
对比单锁的 Hashtable 或 Collections.synchronizedMap():
- Hashtable 整个 map 一把全局锁,所有读写串行化
- ConcurrentHashMap 将锁粒度从“全表”降到“分段”,16 段最多支持 16 路并发写
- 读操作完全无锁,写操作只锁对应段,读写之间也不互斥(除非写导致结构变化并被读感知)
不过 Segment 机制也有代价:内存占用略高(每个 Segment 含锁、table、计数器等)、扩容非全局、复杂度上升;JDK 8 彻底废弃 Segment,改用 CAS + synchronized + 红黑树优化。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










