jdk1.7版本中多线程同时对HashMap扩容时,会引起链表死循环,尽管jdk1.8修复了该问题,但是同样在jdk1.8版本中多线程操作hashMap时仍然会引起死循环,只是原因不一样。
示例代码
package com.gsonkeno.interview;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* jdk7扩容时都可能导致死锁
* jdk8在PutTreeValue时可能死循环 死循环在hashMap的1816行或2229行, java version "1.8.0_111"
* jstack发现可能卡在 at java.util.HashMap$TreeNode.balanceInsertion(HashMap.java:2229)
* 也有可能卡在 at java.util.HashMap$TreeNode.root(HashMap.java:1816)
* @author gaosong
* @since 2019-02-23
*/
public class HashMap1 {
public static void main(String[] args) {
HashMapThread hmt0 = new HashMapThread();
HashMapThread hmt1 = new HashMapThread();
HashMapThread hmt2 = new HashMapThread();
HashMapThread hmt3 = new HashMapThread();
HashMapThread hmt4 = new HashMapThread();
hmt0.start();
hmt1.start();
hmt2.start();
hmt3.start();
hmt4.start();
}
}
class HashMapThread extends Thread
{
private static AtomicInteger ai = new AtomicInteger(0);
private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(1);
@Override
public void run()
{
while (ai.get() < 100000)
{
map.put(ai.get(), ai.get());
ai.incrementAndGet();
}
System.out.println(Thread.currentThread().getName() + "执行结束完");
}
}
代码见github
说明
多个线程全部全部在如下代码块的1816行,一直循环,无法退出for循环。
final TreeNode<K,V> root() {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p; //1816行
}
}
类似地,还有可能卡在at java.util.HashMap$TreeNode.balanceInsertion(HashMap.java:2229)
可能Node节点转换为TreeNode结点异常
主要原因其实是多线程下操作同一对象时,对象内部属性的不一致性导致的。分析方式跟为什么单例要使用双重锁check类似。
总结的经验就是,在多线程下不要使用HashMap,至少jdk8及其以下不要使用,之上版本也建议不要使用。
转载请注明:学时网 » HashMap在jdk1.8中也会死循环