为什么HashMap会产生死循环?

简介: HashMap死循环是一个比较常见、也是比较经典的面试题,在大厂的面试中也经常被问到。HashMap的死循环问题只在JDK1.7版本中会出现,主要是HashMap自身的工作机制,再加上并发操作,从而导致出现死循环。JDK1.8以后,官方彻底解决了这个问题。

HashMap死循环是一个比较常见、也是比较经典的面试题,在大厂的面试中也经常被问到。HashMap的死循环问题只在JDK1.7版本中会出现,主要是HashMap自身的工作机制,再加上并发操作,从而导致出现死循环。JDK1.8以后,官方彻底解决了这个问题。


1、数据插入原理


在分析原因之前,我先带大家了解一下JDK1.7中HashMap插入数据的原理,来看动画演示:

6554c9c4cdbbd9474256ced758cf2524.png

由于JDK 1.7中HashMap的底层存储结构采用的是数组 加 链表的方式。

e5bddda1bf6c70afdd35c636d3c6da84.png

 而HashMap在数据插入时又采用的是头插法,也就是说新插入的数据会从链表的头节点进行插入。

593b3539c0068f96c763f59aefdce4ad.png

因此,HashMap正常情况下的扩容就是是这样一个过程。我们来看,旧HashMap的节点会依次转移到新的HashMap中,旧HashMap转移链表元素的顺序是A、B、C,而新HashMap使用的是头插法插入,所以,扩容完成后最终在新HashMap中链表元素的顺序是C、B、A。


2、导致死循环的原因


接下来,我通过动画演示的方式,带大家彻底理解造成HashMap死循环的原因。我们按以下三个步骤来还原并发场景下HashMap扩容导致的死循环问题:

d9fe5104091c67f41aaea16549c6dfbf.png

第一步:线程启动,有线程T1和线程T2都准备对HashMap进行扩容操作, 此时T1和T2指向的都是链表的头节点A,而T1和T2的下一个节点分别是T1.next和T2.next,它们都指向B节点。

6b2fbc693c90c4ed761916c96e338f89.png

第二步:开始扩容,这时候,假设线程T2的时间片用完,进入了休眠状态,而线程T1开始执行扩容操作,一直到线程T1扩容完成后,线程T2才被唤醒。

758d5388a8de677e37cbf8d611896244.png

T1完成扩容之后的场景就变成动画所示的这样。

75d08d90feb8055f0b4de49d6a3a78bc.png

因为HashMap扩容采用的是头插法,线程T1执行之后,链表中的节点顺序发生了改变。但线程T2对于发生的一切还是不可知的,所以它指向的节点引用依然没变。如图所示,T2指向的是A节点,T2.next指向的是B节点。

d0f6714f82098f34b4460ada4404f8df.png

当线程T1执行完成之后,线程T2恢复执行时,死循环就发生了。

e279199d89fa198b5ccab4a727ae037a.png

因为T1执行完扩容之后,B节点的下一个节点是A,而T2线程指向的首节点是A,第二个节点是B,这个顺序刚好和T1扩容之前的节点顺序是相反的。T1执行完之后的顺序是B到A,而T2的顺序是A到B,这样A节点和B节点就形成了死循环。


3、解决方案


避免HashMap发生死循环的常用解决方案有三个:

   1)、使用线程安全的ConcurrentHashMap替代HashMap,个人推荐使用此方案。


2)、使用线程安全的容器Hashtable替代,但它性能较低,不建议使用。


3)、使用synchronized或Lock加锁之后,再进行操作,相当于多线程排队执行,也会影响性能,不建议使用。


4、总结


HashMap死循环只发生在JDK1.7版本中,主要原因是JDK1.7中的HashMap,在头插法 加 链表 加 多线程并发 加 扩容这几个情形累加到一起就会形成死循环。多线程环境下建议采用ConcurrentHashMap替代。在JDK1.8中,HashMap改成了尾插法,解决了链表死循环的问题。


以上就是关于HashMap死循环原因的分析,听懂的小伙伴,关注点个赞,下次不迷路。


S信【Tom】或【666】即可免费领取需要更多干货内容,还有海量面试资料,只弹干货不惨水!


本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。


相关文章
|
2月前
|
设计模式 安全 Java
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
假如有T1、T2两个线程同时对某链表扩容,他们都标记头结点和第二个结点,此时T2阻塞,T1执行完扩容后链表结点顺序反过来,此时T2恢复运行再进行翻转就会产生环形链表,即B.next=A;采用2的指数进行扩容,是为了利用位运算,提高扩容运算的效率。JDK8中,HashMap采用尾插法,扩容时链表节点位置不会翻转,解决了扩容死循环问题,但是性能差了一点,因为要遍历链表再查到尾部。例如15(即2^4-1)的二进制为1111,31的二进制为11111,63的二进制为111111,127的二进制为1111111。
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
|
1月前
|
Java 调度
HashMap为什么会死循环?
本文分析了在Java中HashMap导致死循环的原因,主要由于在JDK 1.7及之前版本中,多线程环境下进行扩容操作时,头插法导致的链表反转,以及线程调度问题,从而形成循环链表。
19 0
HashMap为什么会死循环?
|
23天前
|
存储 算法 安全
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
HashMap常见面试题:红黑树、散列表,HashMap实现原理、扩容机制,HashMap的jd1.7与jdk1.8有什么区别,寻址算法、链表何时升级为红黑树、死循环
|
12月前
|
算法 安全 Java
hashmap死循环原因总结
hashmap死循环原因总结
52 0
|
算法 安全 Java
【Java面试】HashMap死循环问题
【Java面试】HashMap死循环问题
72 0
|
存储 安全 Java
聊聊hashmap在1.7情况下的多线程死循环问题
在Java 1.7版本中,HashMap的扩容过程存在一个多线程环境下的死循环问题。这个问题的具体原因是由于HashMap在进行扩容时,多个线程同时进行put操作,可能会导致链表形成环形结构,从而导致get操作陷入死循环。
170 0
|
算法 安全 Java
推荐:并发情况下:Java HashMap 形成死循环的原因
推荐:并发情况下:Java HashMap 形成死循环的原因
177 1
|
监控 Java 网络安全
为什么JDK8中HashMap依然会死循环
为什么JDK8中HashMap依然会死循环
为什么JDK8中HashMap依然会死循环
|
Java
天猫面试官:说说高并发下的HashMap的死循环是怎么形成的!
师傅,我常常听别人说,不要在并发情况下使用HashMap,可能会出现死循环,这个死循环是怎么形成的呢?
125 0
|
算法 安全 Java
Java并发编程 - HashMap 死循环
Java并发编程 - HashMap 死循环
172 0
Java并发编程 - HashMap 死循环