在1.8中ConcurrentHashMap采用分段锁和CAS+synchronized的方式保证线程安全,
分段锁采用内部类Segment实现,实现了ReentrantLock方法;
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
putVal方法详解:
final V putVal(K key, V value, boolean onlyIfAbsent) {
//首先会判断 key 和 value 是否为 null,如果是则抛出异常
if (key == null || value == null) throw new NullPointerException();
//然后计算 key 的 hash 值,通过散列函数 spread() 进行扰动计算,得到散列后的值。
int hash = spread(key.hashCode());
//
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
//检查 table 数组是否初始化,如果未初始化则调用 initTable() 进行初始化
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//检查数组对应位置是否为空,如果为空则使用 CAS 尝试插入新节点,成功则跳出方法。这
//是一种乐观锁机制,可以避免获取锁
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // no lock when adding to empty bin
}
//如果数组对应位置的头节点的 hash 值为 MOVED,则说明正在扩容,则帮助其扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
//检查只有当值不存在的情况下,对应的键是否已经存在,如果存在则返回旧值。
else if (onlyIfAbsent // check first node without acquiring lock
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
return fv;
else {
V oldVal = null;
//加锁,插入新节点
synchronized (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);
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;
}
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
//判断链表长度是否达到阈值,如果达到则调用 treeifyBin() 方法将链表转换成红黑树
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//使用 addCount() 方法统计并发修改次数
addCount(1L, binCount);
return null;
}