HashMap在jdk1.8中也会死循环

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

说明

HashMap在jdk1.8中也会死循环_第1张图片多个线程全部全部在如下代码块的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及其以下不要使用,之上版本也建议不要使用。

你可能感兴趣的:(集合,java)