ConcurrentHashMap
是 Java 并发包 (java.util.concurrent
) 中提供的一个线程安全的哈希表实现,它是对 HashMap
的线程安全版本,但比 Hashtable
或 Collections.synchronizedMap()
有更好的并发性能。
目录
Java 8 实现 (CAS + synchronized)
基本特性
-
线程安全:支持多线程并发访问而不需要外部同步
-
高并发:通过分段锁或 CAS 操作实现高并发性能
-
弱一致性:迭代器反映的是创建时的状态,不保证反映所有更新
-
不允许 null 键/值:与
HashMap
不同,不允许 null 键或值
Java 7 实现 (分段锁)
在 Java 7 中,ConcurrentHashMap
使用分段锁技术:
-
将整个哈希表分成多个段 (Segment),每个段是一个独立的哈希表
-
每个段有自己的锁,不同段可以并发操作
-
默认创建 16 个段,并发级别为 16
Java 8 实现 (CAS + synchronized)
Java 8 对 ConcurrentHashMap
进行了重大改进:
-
取消了分段锁设计
-
使用 CAS (Compare-And-Swap) + synchronized 实现更细粒度的锁
-
当发生哈希冲突时,只锁住冲突的链表或红黑树的头节点
-
引入红黑树处理哈希冲突,当链表长度超过阈值(默认为8)时转换为红黑树
重要参数
-
initialCapacity:初始容量,默认16
-
loadFactor:负载因子,默认0.75f
-
concurrencyLevel:并发级别(Java7),Java8中仅用于兼容性
-
TREEIFY_THRESHOLD:链表转红黑树的阈值,默认为8
-
UNTREEIFY_THRESHOLD:红黑树转链表的阈值,默认为6
Java 8 中的并发控制
-
CAS:用于无竞争情况下的快速操作
-
synchronized:当发生哈希冲突时,锁住链表或树的头节点
-
volatile:保证变量的可见性
-
sizeCtl:控制表初始化和扩容的特殊标志
扩容机制
-
当元素数量超过容量*负载因子时触发扩容
-
扩容时创建新表(大小为原表2倍)
-
采用多线程协助扩容机制,提高扩容效率
-
扩容期间可以同时进行查询操作
与其它Map的比较
特性 | HashMap | Hashtable | ConcurrentHashMap |
---|---|---|---|
线程安全 | 否 | 是 | 是 |
锁粒度 | 无锁 | 全表锁 | 分段锁/桶锁 |
允许null键/值 | 是 | 否 | 否 |
迭代器弱一致性 | 否 | 否 | 是 |
性能 | 最高 | 最低 | 高 |
常见问题
为什么使用 synchronized 而不是 ReentrantLock?
-
性能考量:Java 8优化后的synchronized在单bucket竞争时性能更好
-
内存占用:synchronized的JVM内置锁更节省内存
-
JIT优化:锁消除/锁膨胀等优化更成熟
如何保证扩容期间的一致性?
-
ForwardingNode机制:已迁移的bucket会被标记,读操作自动跳转新表
-
写操作协作:遇到ForwardingNode的写入线程会先协助扩容
size()为何不精确?
-
性能权衡:精确计数需要全局锁,采用分片计数(baseCount + CounterCell[])牺牲精确性换取并发度
核心源码解析
设计亮点解析
-
锁粒度优化:仅锁单个bucket头节点(Java 7锁Segment)
-
无锁读路径:所有读操作都不加锁,依赖volatile和final保证可见性
-
协作式扩容:多线程共同完成数据迁移
-
状态机设计:通过MOVED、TREEBIN等特殊hash值实现状态转换
-
缓存友好:使用@Contended避免伪共享
核心数据结构
-
哈希表定义
transient volatile Node<K,V>[] table; // 主哈希表,volatile保证可见性
private transient volatile int sizeCtl; // 控制表初始化和扩容
-
节点类型
基础节点(链表)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // 不可变hash值
final K key; // 不可变key
volatile V val; // volatile保证可见性
volatile Node<K,V> next; // volatile保证可见性
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
}
树节点(红黑树)
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // 红黑树父节点
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // 链表前驱(用于删除)
boolean red;
// 树查找方法
final TreeNode<K,V> find(int h, Object k) {
// 红黑树查找实现...
}
}
并发控制关键代码
原子访问方法
// Unsafe原子操作
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
putVal核心逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 1. 计算hash(二次哈希)
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 2. 表未初始化则初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// 3. 目标bucket为空则CAS插入
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<>(hash, key, value, null)))
break;
}
// 4. 检测到扩容则协助扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
// 5. bucket非空则同步处理
else {
synchronized (f) { // 锁住bucket头节点
if (tabAt(tab, i) == f) {
if (fh >= 0) { // 链表处理
// ...遍历链表...
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i); // 链表转树
}
else if (f instanceof TreeBin) { // 树处理
// ...红黑树插入...
}
}
}
}
}
// 6. 更新size
addCount(1L, binCount);
return null;
}
扩容关键代码
transfer 方法(核心扩容逻辑)
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
// 1. 计算每个线程处理的bucket范围(最小16)
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE;
// 2. 初始化新表(2倍容量)
if (nextTab == null) {
try {
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { ... }
nextTable = nextTab;
transferIndex = n; // 从高位开始迁移
}
// 3. 迁移主循环
while (advance) {
// 分配任务区间(原子更新transferIndex)
if (--i >= bound || finishing)
advance = false;
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ? nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
// 4. 处理单个bucket
if (i < 0 || i >= n || i + n >= nextn) {
// 完成检查...
}
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd); // 标记为空bucket
else if ((fh = f.hash) == MOVED)
advance = true; // 已处理
else {
synchronized (f) { // 锁住bucket头节点
// 链表/树拆分逻辑...
if (f instanceof TreeBin) {
TreeBin<K,V> t = (TreeBin<K,V>)f;
// 红黑树拆分...
}
}
}
}
ForwardingNode 设计
static final class ForwardingNode<K,V> extends Node<K,V> {
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null); // hash=MOVED(-1)
this.nextTable = tab;
}
Node<K,V> find(int h, Object k) {
// 转发到新表查询
outer: for (Node<K,V>[] tab = nextTable;;) {
Node<K,V> e; int n;
if (k == null || tab == null || (n = tab.length) == 0)
return null;
if ((e = tabAt(tab, (n - 1) & h)) == null)
return null;
for (;;) {
int eh; K ek;
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
if (eh < 0) {
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
continue outer;
}
else
return e.find(h, k);
}
if ((e = e.next) == null)
return null;
}
}
}
}
并发计数实现
addCount 方法
private final void addCount(long x, int check) {
CounterCell[] cs; long b, s;
// 1. 尝试直接更新baseCount
if ((cs = counterCells) != null ||
!U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
// 2. 使用CounterCell分散热点
CounterCell c; long v; int m;
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended = U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))) {
fullAddCount(x, uncontended); // 完整计数逻辑
return;
}
if (check <= 1)
return;
s = sumCount();
}
// 3. 检查扩容
if (check >= 0) {
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
// 触发扩容...
}
}
}
HashMap详解 HashLink详解 ArrayList详解 CopyOnWriteArrayList详解 HashSet详解 LinkedHashMap详解 LinkedHashSet详解 TreeSet详解 TreeMap详解