当前位置: 首页 > 科技观察

面试突击:HashMap为什么会产生死循环?

时间:2023-03-19 02:19:03 科技观察

HashMap死循环是一个比较常见和经典的问题,在日常面试中经常出现,下面就用图带大家全面了解死循环的原因。JDK1.7版本就出现了pre-knowledgedeadloop问题。这个问题主要是HashMap自身的运行机制和并发操作导致的死循环。在JDK1.7中,HashMap的底层数据实现是一个数组+链表,如下图:而HashMap在添加数据的时候使用的是头部插入,如下图:HashMap在正常情况下的扩展实现为如下图所示:旧的HashMap的节点会依次转移到新的HashMap中。旧的HashMap的传递顺序是A、B、C,而新的HashMap采用的是头部插入的方式,所以新的HashMap中最终的顺序是C、B、A。如上图所示。有了这些前置知识,让我们看看无限循环是如何诞生的。死循环执行第1步是并发HashMap扩容导致的。并发扩容第一步,线程T1和T2需要对HashMap进行扩容,此时T1和T2指向链表的头节点元素A,T1和T2的下一个节点,即T1.next和T2.next指向节点B,如下图:无限循环执行步骤2无限循环的第二步是线程T2在其时间片耗尽时进入休眠状态,线程T1开始执行执行扩展操作。线程T2在线程T1扩展之前不会被唤醒。展开后的场景如下图所示:从上图可以看出,线程T1执行后,HashMap的顺序因为header的插入方式发生了变化,但是线程T2不知道发生了什么,所以其指向的元素不变,如上图所示,T2指向A元素,T2.next指向的节点为B元素。死循环执行步骤3当线程T1执行完毕,线程T2重新开始执行时,就建立了一个死循环,如下图:因为T1执行完展开后,B节点的下一个节点是A,第一个指向的节点tobyT2thread的节点是A,第二个节点是B,这个顺序刚好和T1展开后的节点顺序相反。T1执行完之后,顺序是从B到A,T2的顺序是从A到B,这样A节点和B节点就形成了死循环,这就是HashMap死循环的原因。解决方案HashMap死循环常见的解决方案有3种:使用线程安全的容器ConcurrentHashMap代替(推荐这种方案)。改用线程安全的容器Hashtable(性能低下,不推荐)。使用synchronized或者Lock对HashMap进行加锁之后,再进行操作,相当于多线程队列执行(比较麻烦,不推荐)。综上所述,JDK1.7版本出现HashMap死循环。形成死循环的原因是HashMap在JDK1.7中使用了header插入方式。表头插入方式+链表+多线程并发+HashMap扩展。这些点加在一起就形成了,为了解决HashMap的死锁,可以改用线程安全的容器ConcurrentHashMap。