HashMap为什么会发生死循环

HashMap死循环只会在JDK1.7中出现,出现的原因主要是因为其自身扩容机制加上并发操作。JDK1.8中已彻底解决该问题。源码可参考:为什么HashMap并发时会引起死循环?_光阴似键的博客-CSDN博客_hashmap为什么会造成死循环

JDK1.7中HashMap以数组加链表的方式存储,在插入数据是采用的是头插法,新插入的数据会从链表的头结点进行插入。因此在HashMap扩容时会存在如下现象:

死循环演示

1. 线程 T1 和线程 T2 要对 HashMap 进行扩容操作,此时 T1 和 T2 指向的是链表的头结点元素 A,而 T1 和 T2 的下一个节点,也就是 T1.next 和 T2.next 指向的是 B 节点,如下图所示:

2.线程 T2 时间片用完进入休眠状态,而线程 T1 开始执行扩容操作,一直到线程 T1 扩容完成后,线程 T2 才被唤醒,扩容之后的场景如下图所示,可见扩容完成后,T2仍指向A,T.next仍指向B。

3.当线程 T1 执行完,而线程 T2 恢复执行时,死循环就建立了,如下图所示:

 线程T1先执行完扩容后,线程T2的指向没有改变,T1 执行完之后的顺序是 B 到 A,而 T2 的顺序是 A 到 B,这样 A 节点和 B 节点就形成死循环了。

解决方案

1)使用线程安全的容器ConcurrentHashMap替代HashMap(推荐,面试可能会引出oncurrentHashMap底层是如何实现线程安全的,jdk7跟8有什么别去,再说说分段锁有什么优缺点) 

2)使用线程安全的容器Hashtable替代(性能低,不建议,可能会引出为什么性能低)

3)使用 synchronized 或 Lock 加锁 HashMap 之后,再进行操作,相当于多线程排队执行(比较麻烦,也不建议使用)

总结

HashMap 死循环发生在 JDK 1.7 版本中

主要原因: 头插法 + 链表 + 多线程并发 + 扩容,累加到一起形成了 HashMap 的死循环

解决方案:多线程下建议使用 ConcurrentHashMap 替代

JDK1.8中,HashMap改成了尾插法,解决了链表死循环问题

猜你喜欢

转载自blog.csdn.net/Ahuuua/article/details/124461602