ConcurrentHashMap 底层:初始化流程¶
一、构造函数¶
public ConcurrentHashMap() {
// 默认初始容量 16,加载因子 0.75
}
public ConcurrentHashMap(int initialCapacity) {
// 找大于等于 initialCapacity 的最小 2 的幂
}
二、JDK 8 结构¶
三、懒初始化¶
构造函数不初始化 table,第一次 put 才初始化:
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // 别的线程在初始化,让出
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2); // 0.75n
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
四、sizeCtl 的含义¶
| 值 | 含义 |
|---|---|
| -1 | 正在初始化 |
| -(1 + 扩容线程数) | 正在扩容 |
| 正数 | 下一次扩容阈值 |
| 0 | 默认 |
五、为什么懒初始化¶
和 HashMap 一样,省内存。很多 ConcurrentHashMap 创建了但不一定用,第一次 put 才分配数组。
六、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;
}
链表节点,val 和 next 都是 volatile。
对比 HashMap
- HashMap:构造函数可能初始化数组。
- ConcurrentHashMap:懒初始化,CAS 控制并发初始化。