赞
踩
下面聊聊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()); } }
我们使用jps和jstack来进行分析
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添加节点或者树化的时候导致父子节点之间互连然后导致的死循环,但是什么原因还是不得而知。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。