问题
ConcurrentHashMap是如何实现的?
答案
一、核心概念
ConcurrentHashMap 是 Java 并发包中提供的线程安全的哈希表实现,它在保证线程安全的同时,提供了比 Hashtable 和 Collections.synchronizedMap 更高的并发性能。
二、JDK 1.8 实现原理(主流版本)
1. 数据结构
// 核心字段
transient volatile Node<K,V>[] table; // 哈希表数组
private transient volatile int sizeCtl; // 控制标识符,负数表示正在初始化或扩容
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val; // volatile保证可见性
volatile Node<K,V> next;
}
采用数组 + 链表 + 红黑树结构:
- 数组:存储 Node 节点
- 链表:hash 冲突时,长度小于 8 使用链表
- 红黑树:链表长度达到 8 且数组长度 ≥ 64 时转换为红黑树
2. 锁机制:CAS + synchronized
final V putVal(K key, V value, boolean onlyIfAbsent) {
// ...
for (Node<K,V>[] tab = table;;) {
if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 位置为空,使用CAS无锁插入
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break;
} else {
// 位置有元素,使用synchronized锁住该位置(Node节点)
synchronized (f) {
// 链表或红黑树插入逻辑
// ...
}
}
}
}
关键点:
- CAS 无锁操作:当桶位为空时,使用 CAS 直接插入,避免加锁
- synchronized 锁粒度:锁的是数组中的单个 Node 节点(首节点),而不是整个数组
- 锁分离:不同桶位的操作互不影响,支持高并发
3. get 操作无锁
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e;
int h = spread(key.hashCode());
if ((tab = table) != null && (e = tabAt(tab, (tab.length - 1) & h)) != null) {
// 通过volatile读取,无需加锁
// ...
}
return null;
}
原因:
Node.val和Node.next都是 volatile 修饰,保证可见性- 读操作不会修改数据,可安全并发读取
4. 扩容机制
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
// 支持多线程协作扩容
// 每个线程负责一段数组的迁移
}
特点:
- 多线程协作扩容:多个线程可以同时参与扩容,提高效率
- ForwardingNode 标记:已迁移的桶位用 ForwardingNode 占位,指向新数组
三、JDK 1.7 对比(面试补充)
JDK 1.7 采用分段锁(Segment):
// JDK 1.7 结构
Segment<K,V>[] segments; // 默认16个Segment
static class Segment<K,V> extends ReentrantLock { // 继承锁
transient HashEntry<K,V>[] table;
}
缺点:
- 最大并发度受限于 Segment 数量(默认16)
- 锁粒度较粗,多个桶位共享一把锁
四、性能优化与线程安全保证
- volatile + CAS:保证可见性和原子性,减少锁竞争
- 锁粒度细化:从 Segment 级别降低到 Node 级别
- 读操作无锁:利用 volatile 特性,提升并发读性能
- 红黑树优化:避免极端情况下链表过长导致性能下降
五、答题总结
ConcurrentHashMap(JDK 1.8)通过以下方式实现高效并发:
- 数据结构:数组 + 链表 / 红黑树,与 HashMap 类似
- 锁机制:CAS(空桶位)+ synchronized(有冲突时锁 Node)
- 锁粒度:细化到数组单个元素级别,支持更高并发
- 读操作:利用 volatile 实现无锁读取
- 扩容:支持多线程协作扩容
相比 JDK 1.7 的分段锁,1.8 版本提升了并发度,降低了锁竞争,性能更优。