美文网首页
问题:ConcurrentHashMap1.8的一个死循环bug

问题:ConcurrentHashMap1.8的一个死循环bug

作者: 栢鸽 | 来源:发表于2019-05-11 15:13 被阅读0次

    bug复现

    public class ConcurrentHashMapDemo {
    
        public static void main(String[] args) {
            ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(16);
            map.computeIfAbsent(
                    "AaAa",
                    v ->  map.computeIfAbsent("BBBB", v2 -> 42)
            );
        }
    
    
    }
    

    bug发生条件:

    1. 第一次执行computeIfAbsent发现槽点没有值,就新建ReservationNode节点,并插入
    2. 第一次与第二次执行computeIfAbsent的key的hashCode%容量相等

    我们看下源码会发生什么情况

    第一次执行computeIfAbsent

    看如下代码,第一次执行computeIfAbsent时新建并插入ReservationNode

        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)
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                    // 第一次执行该方法,新建了一个Node,并插入槽中
                    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);
                            }
                        }
                    }
                    if (binCount != 0)
                        break;
                }
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);
                else {
                    boolean added = false;
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            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);
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (!added)
                            return val;
                        break;
                    }
                }
            }
            if (val != null)
                addCount(1L, binCount);
            return val;
        }
    
    

    第二次执行时

    在如下注释地方进入死循环

        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)
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                    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);
                            }
                        }
                    }
                    if (binCount != 0)
                        break;
                }
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);
                else {
                    // 第二次执行时,进入该条件
                    boolean added = false;
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            // ReservationNode节点的fh == -3,也不是TreeBin
                            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);
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (!added)
                            return val;
                        break;
                    }
                }
            }
            if (val != null)
                addCount(1L, binCount);
            return val;
        }
    
    

    1.9 已经修复该bug

    https://bugs.openjdk.java.net/browse/JDK-8172951

    相关文章

      网友评论

          本文标题:问题:ConcurrentHashMap1.8的一个死循环bug

          本文链接:https://www.haomeiwen.com/subject/ojvxaqtx.html