JDK1.8的HashMap死循环复现

文章目录

  • 前言
  • 正文


前言

下面聊聊JDK1.7HashMap的死循环问题,在这之前首先要知道JDK1.7的HashMap底层是数组 + 链表的形式的


正文

JDK1.8解决了JDK1.7的头插法导致死循环的问题,但是JDK1.8同样会死循环,下面我们用两个线程进行演示

abstract class Test {
    public static void main(String[] args) throws InterruptedException {
        Map<String,Integer> map = new HashMap<>();
        Thread t1 = new Thread(() -> {
            //添加奇数
            for (int i = 1; i < 2000000; i += 2) {
                map.put(Integer.toString(i), i);
                System.out.println(Thread.currentThread() + ":" + i);
            }
        });
        Thread t2 = new Thread(()->{
            //添加偶数
            for(int i = 0 ; i<2000000;i+=2){
                map.put(Integer.toString(i),i);
                System.out.println(Thread.currentThread() + ":" + i);
            }
        });
        t1.start();
        t2.start();
        //主线程等待t1和t2执行完成
        t1.join();
        t2.join();
        System.out.println(map.size());
    }
}

JDK1.8的HashMap死循环复现_第1张图片
我们使用jps和jstack来进行分析
JDK1.8的HashMap死循环复现_第2张图片

D:\javaCode\LeedCode>jstack 63556
2023-01-23 02:57:16
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode):

"Thread-1" #13 prio=5 os_prio=0 tid=0x0000023ef0e98800 nid=0xe648 runnable [0x00000073318ff000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap$TreeNode.treeify(HashMap.java:1948)
        at java.util.HashMap$TreeNode.split(HashMap.java:2180)
        at java.util.HashMap.resize(HashMap.java:714)
        at java.util.HashMap.putVal(HashMap.java:663)
        at java.util.HashMap.put(HashMap.java:612)
        at com.jianglianghao.mianshi_test.Test.lambda$main$1(Test.java:26)
        at com.jianglianghao.mianshi_test.Test$$Lambda$2/2093631819.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0" #12 prio=5 os_prio=0 tid=0x0000023ef0e98000 nid=0xf2cc runnable [0x00000073317ff000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap$TreeNode.treeify(HashMap.java:1948)
        at java.util.HashMap$TreeNode.split(HashMap.java:2180)
        at java.util.HashMap.resize(HashMap.java:714)
        at java.util.HashMap.putVal(HashMap.java:663)
        at java.util.HashMap.put(HashMap.java:612)
        at com.jianglianghao.mianshi_test.Test.lambda$main$0(Test.java:19)
        at com.jianglianghao.mianshi_test.Test$$Lambda$1/558638686.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

追溯到代码层面发现是在树化的时候导致的死循环,后续也测了几次,发现死循环的几次的代码不同,下面这一次的死循环出现在putTrereVal,也就是添加树节点这里,而第二个线程卡在了balanceInsertion

D:\javaCode\LeedCode>jstack 64008
2023-01-23 02:40:20
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode):

"Thread-1" #13 prio=5 os_prio=0 tid=0x000002d51df7a000 nid=0xf834 runnable [0x000000dba0dfe000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap$TreeNode.root(HashMap.java:1824)
        at java.util.HashMap$TreeNode.putTreeVal(HashMap.java:1978)
        at java.util.HashMap.putVal(HashMap.java:638)
        at java.util.HashMap.put(HashMap.java:612)
        at com.jianglianghao.mianshi_test.Test$MyTask.run(Test.java:23)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0" #12 prio=5 os_prio=0 tid=0x000002d51df71000 nid=0xe5a0 runnable [0x000000dba0cfe000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap$TreeNode.balanceInsertion(HashMap.java:2239)
        at java.util.HashMap$TreeNode.treeify(HashMap.java:1945)
        at java.util.HashMap$TreeNode.split(HashMap.java:2180)
        at java.util.HashMap.resize(HashMap.java:714)
        at java.util.HashMap.putVal(HashMap.java:663)
        at java.util.HashMap.put(HashMap.java:612)
        at com.jianglianghao.mianshi_test.Test$MyTask.run(Test.java:23)
        at java.lang.Thread.run(Thread.java:748)

final TreeNode<K,V> root() {
    for (TreeNode<K,V> r = this, p;;) {
        if ((p = r.parent) == null)
            return r;
        r = p;
    }
}


static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
            x.red = true;
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                else if (!xp.red || (xpp = xp.parent) == null)
                    return root;
                if (xp == (xppl = xpp.left)) {
                    if ((xppr = xpp.right) != null && xppr.red) {
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.right) {
                            root = rotateLeft(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

其实从root方法中也能看出来了肯定是在putVal添加节点或者树化的时候导致父子节点之间互连然后导致的死循环,但是什么原因还是不得而知。

你可能感兴趣的:(源码分析,java,数据结构,数据库)