1. 概述
ConcurrentHashMap是Java中的线程安全的哈希表实现,它在JDK 1.7和1.8中有着完全不同的实现方式。本文将深入分析JDK 1.8中ConcurrentHashMap的实现原理。
2. 重要的内部结构
2.1 核心成员变量
// 存储数据的数组,懒加载
transient volatile Node<K,V>[] table;
// 扩容时用于存储数据的数组
private transient volatile Node<K,V>[] nextTable;
// 基础计数器值
private transient volatile long baseCount;
// 控制表初始化和扩容的标志位
private transient volatile int sizeCtl;
// 扩容时的标记位
private transient volatile int transferIndex;
2.2 Node节点结构
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // key的hash值
final K key; // key
volatile V val; // value
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;
}
}
3. 关键技术点分析
3.1 初始化过程
ConcurrentHashMap采用懒加载的方式,在第一次插入元素时才会初始化table数组:
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
// sizeCtl < 0 表示有其他线程在初始化
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;
sc = n - (n >>> 2); // 0.75n
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
3.2 put操作的实现
put操作的主要流程:
- 计算key的hash值
- 如果table为空则初始化
- 如果要插入的位置为空,直接CAS写入
- 如果当前正在扩容,则协助扩容
- 如果存在hash冲突,则使用synchronized锁住当前桶
- 如果是链表结构,则遍历链表更新或插入
- 如果是红黑树结构,则按照红黑树规则更新或插入
关键代码:
final