在Java8的ConcurrentHashMap的computeIfAbsent/putIfAbsent方法中嵌套调用,可能会出现死循环问题:
java
public static void main(String[] args) throws ExecutionException, InterruptedException {
Map<String, List<String>> map = new ConcurrentHashMap<>();
System.out.println("main started");
// 外层computeIfAbsent
List<String> list = map.computeIfAbsent("a", key -> {
System.out.println("out computeIfAbsent started");
// 内层再次computeIfAbsent,且key和外层保持一致(只要hashcode一致都会有这个问题)
List<String> innerList = map.computeIfAbsent("a", innerKey -> {
System.out.println("inner computeIfAbsent started");
List<String> results = new ArrayList<>();
System.out.println("inner computeIfAbsent end");
return results;
});
innerList.add("1");
System.out.println("out computeIfAbsent end");
return innerList;
});
list.add("2");
System.out.println("main end");
}
java8执行执行会死锁一直等待:
main started
out computeIfAbsent started
Process finished with exit code 130 (interrupted by signal 2:SIGINT)
Java9及之后会抛异常:
main started
out computeIfAbsent started
Exception in thread "main" java.lang.IllegalStateException: Recursive update
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1760)
at TestAviator.lambda$main$1(TestAviator.java:198)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
at TestAviator.main(TestAviator.java:196)
Java8源码解读:
java
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (key == null || mappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 1. 外层computeIfAbsent的第一次for循环先初始化table;
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
// 2. 外层computeIfAbsent的第二次for循环走到这里;
// 2. 内层computeIfAbsent的for循环都不走到这里
// 2. 对应的slot不为空,则new一个新的链表节点;
// 注意这里初始化了一个类似"标兵节点",ReservationNode,其中hashcode==RESERVED < 0
Node<K,V> r = new ReservationNode<K,V>();
// synchronized同一个线程可重入
synchronized (r) {
// cas: null -> r
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
// 3. 第一次computeIfAbsent会从mappingFunction.apply走到内层computeIfAbsent
// 注意,此时slot中的头节点还没set,还是null
if ((val = mappingFunction.apply(key)) != null)
// 构造真实Node
node = new Node<K,V>(h, key, val, null);
} finally {
// 替换掉ReservationNode
setTabAt(tab, i, node);
}
}
}
if (binCount != 0)
break;
}
// 4. 设置fh值
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// 5. 内层computeIfAbsent会走到这里
boolean added = false;
// synchronized同一个线程可重入
synchronized (f) {
if (tabAt(tab, i) == f) {
// 6. 这里一直为false,因为头节点还是ReservationNode,其hashcode字段(即fh)<0, 会一直for循环下去
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek; V ev;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = e.val;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
if ((val = mappingFunction.apply(key)) != null) {
added = true;
pred.next = new Node<K,V>(h, key, val, null);
}
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null)
val = p.val;
else if ((val = mappingFunction.apply(key)) != null) {
added = true;
t.putTreeVal(h, key, val);
}
}
// ***** 这里的 else if 是java9之后加的,会直接抛异常 *****
// else if (f instanceof ReservationNode)
// throw new IllegalStateException("Recursive update");
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (!added)
return val;
break;
}
}
}
if (val != null)
addCount(1L, binCount);
return val;
}