并发HashMap实现机制
1. 概述
ConcurrentHashMap是Java并发包中提供的线程安全的哈希表实现,它在保证线程安全的同时,提供了比Hashtable更好的并发性能。本文将深入分析其实现机制,包括数据结构、核心算法和并发控制策略。
2. 简单使用例子
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// 创建ConcurrentHashMap实例
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 创建线程池模拟并发操作
ExecutorService executor = Executors.newFixedThreadPool(4);
// 并发添加元素
for (int i = 0; i < 100; i++) {
final int value = i;
executor.submit(() -> {
map.put("key" + value, value);
System.out.println("Added: key" + value + " = " + value);
});
}
// 并发读取元素
for (int i = 0; i < 50; i++) {
final int value = i;
executor.submit(() -> {
Integer result = map.get("key" + value);
System.out.println("Read: key" + value + " = " + result);
});
}
// 原子操作示例
map.putIfAbsent("atomicKey", 100);
map.replace("key1", 1, 101);
map.computeIfAbsent("computeKey", k -> k.length());
executor.shutdown();
}
}
3. 核心接口定义
3.1 ConcurrentMap接口
ConcurrentMap接口继承自Map接口,提供了线程安全的原子操作方法:
public interface ConcurrentMap<K,V> extends Map<K,V> {
// 原子性的条件插入操作
V putIfAbsent(K key, V value);
// 原子性的条件删除操作
boolean remove(Object key, Object value);
// 原子性的条件替换操作
boolean replace(K key, V oldValue, V newValue);
// 原子性的替换操作
V replace(K key, V value);
// 计算型操作
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction);
}
4. 内存结构分析
4.1 核心数据结构
ConcurrentHashMap的内存结构主要包括以下组件:
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
// 哈希表数组,存储Node节点
transient volatile Node<K,V>[] table;
// 扩容时的新表
private transient volatile Node<K,V>[] nextTable;
// 元素计数基础值
private transient volatile long baseCount;
// 控制表初始化和扩容的字段
// 负数表示正在初始化或扩容:-1表示初始化,-(1+扩容线程数)表示扩容
// 正数表示下次扩容的阈值
private transient volatile int sizeCtl;
// 扩容时的转移索引
private transient volatile int transferIndex;
// 计数器数组,用于减少竞争
private transient volatile CounterCell[] counterCells;
}
4.2 Node节点结构
// 基本节点类型
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(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 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;
}
}
// 红黑树节点
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;
}
4.3 内存布局图
graph TB
subgraph "ConcurrentHashMap内存结构"
CHM["ConcurrentHashMap"]
Table["table: Node[]"]
NextTable["nextTable: Node[]"]
SizeCtl["sizeCtl: int"]
BaseCount["baseCount: long"]
CounterCells["counterCells: CounterCell[]"]
CHM --> Table
CHM --> NextTable
CHM --> SizeCtl
CHM --> BaseCount
CHM --> CounterCells
subgraph "桶结构"
Bucket0["bucket[0]"]
Bucket1["bucket[1]"]
BucketN["bucket[n]"]
Table --> Bucket0
Table --> Bucket1
Table --> BucketN
end
subgraph "链表结构"
Node1["Node1"]
Node2["Node2"]
Node3["Node3"]
Bucket0 --> Node1
Node1 --> Node2
Node2 --> Node3
end
subgraph "红黑树结构"
TreeBin["TreeBin"]
TreeNode1["TreeNode"]
TreeNode2["TreeNode"]
TreeNode3["TreeNode"]
Bucket1 --> TreeBin
TreeBin --> TreeNode1
TreeNode1 --> TreeNode2
TreeNode1 --> TreeNode3
end
end
5. 核心源码分析
5.1 初始化机制
/**
* 初始化哈希表
* 使用sizeCtl控制并发初始化,确保只有一个线程能够初始化表
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
// 循环直到表被成功初始化
while ((tab = table) == null || tab.length == 0) {
// 如果sizeCtl < 0,说明其他线程正在初始化,当前线程让出CPU
if ((sc = sizeCtl) < 0)
Thread.yield(); // 让出CPU时间片
// 尝试CAS设置sizeCtl为-1,表示当前线程正在初始化
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
// 双重检查,确保表仍未初始化
if ((tab = table) == null || tab.length == 0) {
// 确定初始容量
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
// 设置下次扩容阈值为容量的0.75倍
sc = n - (n >>> 2);
}
} finally {
// 恢复sizeCtl为扩容阈值
sizeCtl = sc;
}
break;
}
}
return tab;
}
5.2 添加元素机制
/**
* 添加键值对的核心实现
* @param key 键
* @param value 值
* @param onlyIfAbsent 如果为true,不替换已存在的值
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
// ConcurrentHashMap不允许null键或null值
if (key == null || value == null) throw new NullPointerException();
// 计算哈希值,使用spread方法确保哈希值为正数
int hash = spread(key.hashCode());
int binCount = 0; // 记录桶中节点数量
// 无限循环,直到成功插入
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 如果表未初始化,先初始化表
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// 如果目标桶为空,使用CAS直接插入新节点
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break; // 插入成功,跳出循环
}
// 如果遇到转发节点,说明正在扩容,协助扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 锁定桶的第一个节点,进行同步操作
synchronized (f) {
// 双重检查,确保f仍是桶的第一个节点
if (tabAt(tab, i) == f) {
// 处理链表结构
if (fh >= 0) {
binCount = 1;
// 遍历链表
for (Node<K,V> e = f;; ++binCount) {
K ek;
// 找到相同的键,更新值
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
// 到达链表末尾,插入新节点
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key, value, null);
break;
}
}
}
// 处理红黑树结构
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
// 在红黑树中插入节点
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
// 检查是否需要树化
if (binCount != 0) {
// 如果链表长度达到树化阈值,转换为红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
// 增加元素计数,检查是否需要扩容
addCount(1L, binCount);
return null;
}
5.3 查找元素机制
/**
* 获取指定键对应的值
* @param key 要查找的键
* @return 对应的值,如果不存在则返回null
*/
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
// 计算哈希值
int h = spread(key.hashCode());
// 检查表是否已初始化且不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
// 检查桶的第一个节点
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
// 如果是特殊节点(转发节点或树根节点)
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
// 遍历链表查找
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
5.4 扩容机制
/**
* 扩容操作的核心实现
* 将元素从旧表迁移到新表,支持多线程协作
*/
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
// 计算每个线程处理的桶数量,最少16个
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE;
// 如果新表为空,创建新表(容量翻倍)
if (nextTab == null) {
try {
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) {
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
transferIndex = n; // 设置转移索引
}
int nextn = nextTab.length;
// 创建转发节点,标记已迁移的桶
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
boolean advance = true; // 是否继续处理下一个桶
boolean finishing = false; // 是否完成扩容
// 主循环:处理每个桶的迁移
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
// 获取下一个需要处理的桶索引
while (advance) {
int nextIndex, nextBound;
if (--i >= bound || finishing)
advance = false;
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
// CAS更新transferIndex,获取处理范围
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
// 检查是否完成所有桶的迁移
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
if (finishing) {
// 扩容完成,更新table引用
nextTable = null;
table = nextTab;
sizeCtl = (n << 1) - (n >>> 1); // 设置新的扩容阈值
return;
}
// 减少扩容线程计数
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
i = n; // 重新检查
}
continue;
}
// 处理空桶
if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
// 桶已被其他线程处理
else if ((fh = f.hash) == MOVED)
advance = true;
else {
// 锁定桶,进行迁移
synchronized (f) {
if (tabAt(tab, i) == f) {
Node<K,V> ln, hn; // 低位链表和高位链表
// 处理普通链表节点
if (fh >= 0) {
int runBit = fh & n; // 计算在新表中的位置
Node<K,V> lastRun = f;
// 找到最后一段连续的相同runBit的节点
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
// 设置低位或高位链表的起始节点
if (runBit == 0) {
ln = lastRun;
hn = null;
} else {
hn = lastRun;
ln = null;
}
// 构建低位和高位链表
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
// 将链表放入新表的对应位置
setTabAt(nextTab, i, ln); // 低位:原位置
setTabAt(nextTab, i + n, hn); // 高位:原位置+n
setTabAt(tab, i, fwd); // 标记原桶已迁移
advance = true;
}
// 处理红黑树节点
else if (f instanceof TreeBin) {
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
// 遍历红黑树,分离为两个链表
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
++lc;
} else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
// 根据节点数量决定是否保持树结构
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
// 将结果放入新表
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
6. 模拟数据和操作流程
6.1 初始化流程
sequenceDiagram
participant T1 as Thread1
participant T2 as Thread2
participant CHM as ConcurrentHashMap
participant Table as table[]
T1->>CHM: new ConcurrentHashMap()
CHM->>CHM: sizeCtl = DEFAULT_CAPACITY
CHM->>CHM: table = null
T1->>CHM: put("key1", "value1")
CHM->>CHM: table == null, 需要初始化
CHM->>CHM: CAS(sizeCtl, 16, -1) 成功
CHM->>Table: 创建 Node[16]
CHM->>CHM: sizeCtl = 12 (0.75 * 16)
CHM->>Table: table = 新数组
T2->>CHM: put("key2", "value2")
CHM->>CHM: table != null, 直接使用
6.2 添加元素流程
假设我们有一个初始容量为4的ConcurrentHashMap,演示添加元素的过程:
初始状态
graph LR
subgraph "ConcurrentHashMap初始状态"
Table["table[4]"]
B0["bucket[0]: null"]
B1["bucket[1]: null"]
B2["bucket[2]: null"]
B3["bucket[3]: null"]
Table --> B0
Table --> B1
Table --> B2
Table --> B3
end
SizeCtl["sizeCtl: 3"]
BaseCount["baseCount: 0"]
添加第一个元素 put("key1", "value1")
// 假设 "key1".hashCode() = 1001
// spread(1001) = 1001 & 0x7fffffff = 1001
// 桶索引 = 1001 & (4-1) = 1001 & 3 = 1
graph LR
subgraph "添加key1后的状态"
Table["table[4]"]
B0["bucket[0]: null"]
B1["bucket[1]"]
B2["bucket[2]: null"]
B3["bucket[3]: null"]
Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:null"]
Table --> B0
Table --> B1
Table --> B2
Table --> B3
B1 --> Node1
end
SizeCtl["sizeCtl: 3"]
BaseCount["baseCount: 1"]
添加第二个元素 put("key2", "value2") - 无冲突
// 假设 "key2".hashCode() = 2002
// spread(2002) = 2002
// 桶索引 = 2002 & 3 = 2
graph LR
subgraph "添加key2后的状态"
Table["table[4]"]
B0["bucket[0]: null"]
B1["bucket[1]"]
B2["bucket[2]"]
B3["bucket[3]: null"]
Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:null"]
Node2["Node<br/>hash:2002<br/>key:'key2'<br/>val:'value2'<br/>next:null"]
Table --> B0
Table --> B1
Table --> B2
Table --> B3
B1 --> Node1
B2 --> Node2
end
SizeCtl["sizeCtl: 3"]
BaseCount["baseCount: 2"]
添加第三个元素 put("key3", "value3") - 发生冲突
// 假设 "key3".hashCode() = 1005
// spread(1005) = 1005
// 桶索引 = 1005 & 3 = 1 (与key1冲突)
graph LR
subgraph "添加key3后的状态(链表)"
Table["table[4]"]
B0["bucket[0]: null"]
B1["bucket[1]"]
B2["bucket[2]"]
B3["bucket[3]: null"]
Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:→"]
Node3["Node<br/>hash:1005<br/>key:'key3'<br/>val:'value3'<br/>next:null"]
Node2["Node<br/>hash:2002<br/>key:'key2'<br/>val:'value2'<br/>next:null"]
Table --> B0
Table --> B1
Table --> B2
Table --> B3
B1 --> Node1
Node1 --> Node3
B2 --> Node2
end
SizeCtl["sizeCtl: 3"]
BaseCount["baseCount: 3"]
添加第四个元素触发扩容
// 当前元素数量(3) >= sizeCtl(3),触发扩容
// 新容量 = 4 * 2 = 8
// 新的sizeCtl = 8 * 0.75 = 6
graph TB
subgraph "扩容过程"
OldTable["旧表 table[4]"]
NewTable["新表 nextTable[8]"]
subgraph "旧表内容"
OB0["[0]: null"]
OB1["[1]: key1→key3"]
OB2["[2]: key2"]
OB3["[3]: null"]
end
subgraph "新表内容"
NB0["[0]: null"]
NB1["[1]: key1"]
NB2["[2]: key2"]
NB3["[3]: null"]
NB4["[4]: null"]
NB5["[5]: key3"]
NB6["[6]: null"]
NB7["[7]: null"]
end
OldTable --> OB0
OldTable --> OB1
OldTable --> OB2
OldTable --> OB3
NewTable --> NB0
NewTable --> NB1
NewTable --> NB2
NewTable --> NB3
NewTable --> NB4
NewTable --> NB5
NewTable --> NB6
NewTable --> NB7
OB1 -."迁移".-> NB1
OB1 -."迁移".-> NB5
OB2 -."迁移".-> NB2
end
6.3 删除元素流程
删除操作 remove("key3")
sequenceDiagram
participant Client as 客户端
participant CHM as ConcurrentHashMap
participant Bucket as bucket[5]
participant Node as Node链表
Client->>CHM: remove("key3")
CHM->>CHM: 计算hash = spread("key3".hashCode())
CHM->>CHM: 计算索引 i = hash & (length-1) = 5
CHM->>Bucket: 获取bucket[5]的头节点
CHM->>Bucket: synchronized(头节点)
CHM->>Node: 遍历链表查找key3
Node->>CHM: 找到匹配节点
CHM->>Node: 从链表中移除节点
CHM->>CHM: 更新计数器 baseCount--
CHM->>Client: 返回被删除的值
6.4 查找元素流程
查找操作 get("key1")
sequenceDiagram
participant Client as 客户端
participant CHM as ConcurrentHashMap
participant Table as table[]
participant Node as Node
Client->>CHM: get("key1")
CHM->>CHM: 计算hash = spread("key1".hashCode())
CHM->>CHM: 计算索引 i = hash & (length-1)
CHM->>Table: 获取table[i]的头节点
alt 头节点就是目标
Table->>CHM: 头节点key匹配
CHM->>Client: 返回头节点的值
else 需要遍历链表
CHM->>Node: 遍历next指针
Node->>CHM: 找到匹配的key
CHM->>Client: 返回节点的值
else 红黑树查找
CHM->>Node: 调用TreeBin.find()
Node->>CHM: 在红黑树中查找
CHM->>Client: 返回找到的值
end
7. 并发控制机制
7.1 分段锁策略
ConcurrentHashMap使用桶级别的锁,而不是整个表的锁:
// 锁定桶的第一个节点
synchronized (f) { // f是桶的头节点
// 在这个同步块中,只有当前桶被锁定
// 其他桶的操作不受影响
if (tabAt(tab, i) == f) { // 双重检查
// 执行插入、删除或更新操作
}
}
7.2 CAS无锁操作
// 原子性地设置数组元素
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
// 原子性地获取数组元素
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int index) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)index << ASHIFT) + ABASE);
}
7.3 计数器机制
/**
* 计数器单元,用于减少计数竞争
*/
@sun.misc.Contended static final class CounterCell {
volatile long value; // 计数值
CounterCell(long x) { value = x; }
}
/**
* 增加计数的实现
* 优先使用baseCount,发生竞争时使用CounterCell数组
*/
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
// 尝试直接更新baseCount
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
boolean uncontended = true;
// 如果baseCount更新失败,使用CounterCell
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
// 如果CounterCell也竞争激烈,进入fullAddCount
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
s = sumCount(); // 重新计算总数
}
// 检查是否需要扩容
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
// 正在扩容,尝试协助
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
// 增加扩容线程计数
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
// 开始扩容
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
8. 性能优化特性
8.1 红黑树优化
当链表长度超过8且表容量大于64时,链表会转换为红黑树:
/**
* 将链表转换为红黑树
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
// 如果表容量小于64,优先扩容而不是树化
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
// 将链表节点转换为树节点
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
// 创建TreeBin并设置到桶中
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
8.2 协助扩容机制
多个线程可以协助进行扩容操作,提高扩容效率:
/**
* 协助扩容
*/
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
int rs = resizeStamp(tab.length);
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
// 增加协助线程计数
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
9. 总结
ConcurrentHashMap通过以下关键技术实现了高效的并发控制:
- 分段锁机制:使用桶级别的锁,减少锁竞争
- CAS无锁操作:在无竞争情况下避免加锁开销
- 协助扩容:多线程协作进行扩容,提高扩容效率
- 红黑树优化:长链表转换为红黑树,优化查找性能
- 分离计数器:使用CounterCell数组减少计数竞争
- 内存可见性:使用volatile关键字保证内存可见性
这些设计使得ConcurrentHashMap在保证线程安全的同时,提供了优秀的并发性能,是Java并发编程中的重要工具。
关键性能指标
- 读操作:几乎无锁,性能接近HashMap
- 写操作:只锁定相关桶,并发度高
- 扩容操作:支持多线程协助,扩容效率高
- 内存开销:相比Hashtable更低,相比HashMap略高
通过深入理解ConcurrentHashMap的实现原理,我们可以更好地在并发场景中使用这个强大的数据结构。