Java8(java version "1.8.0_201") ConcurrentHashMap 死循环问题

复现代码:

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.computeIfAbsent("a",key->{
    map.put("a", "v2");
    return "v1";
});

导致问题的原因:computeIfAbsent执行过程中,当key对应的节点不存在时,

  1. 会先创建一个ReservationNode对象 r 作为占位符,放到hash桶的对应位置;
  2. synchronized(r) 在synchronized代码块中执行Lambda表达式计算相应的value
Node<K,V> r = new ReservationNode<K,V>();
synchronized (r) {
    if (casTabAt(tab, i, null, r)) {
        binCount = 1;
        Node<K,V> node = null;
        try {
            if ((val = mappingFunction.apply(key)) != null)
                node = new Node<K,V>(h, key, val, null);
        } finally {
            setTabAt(tab, i, node);
        }
    }
}
  1. lambdas代码执行:
map.put("a", "v2");
return "v1";

map.put ==> endless loop

//endless loop
for (Node<K,V>[] tab = table;;) {
    Node<K,V> f; int n, i, fh;
    ...
    //获取直接设置的占位符对象ReservationNode
    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null,
                     new Node<K,V>(hash, key, value, null)))
            break;                   // no lock when adding to empty bin
    }
    //key和value均为null时,ReservationNode对象的hashCode=-3
    else if ((fh = f.hash) == MOVED)
        tab = helpTransfer(tab, f);
    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, 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;
        }
    }
}

你可能感兴趣的:(java)