0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么HashMap会产生死循环呢?

CodeSheep 来源:Java面试真题解析 2023-12-21 09:06 次阅读

HashMap 死循环是一个比较常见、比较经典的问题,在日常的面试中出现的频率比较高,今天这篇文章咱们就通过图解的方式,带大家彻底梳理和理解一下这个问题

前置知识

首先我们要了解一下为什么会有这个问题的发生?

死循环问题发生在 JDK 1.7 版本中,造成这个问题主要是由于 HashMap 自身的运行机制,加上并发操作,从而导致了死循环。在 JDK 1.7 中 HashMap 的底层数据实现是数组 + 链表的方式,如下图所示:efb8482e-9f9c-11ee-8b88-92fbcf53809c.png

而 HashMap 在数据添加时使用的是头插入,如下图所示:

efbc58b0-9f9c-11ee-8b88-92fbcf53809c.png

HashMap 正常情况下的扩容实现如下图所示:

efc7a058-9f9c-11ee-8b88-92fbcf53809c.png

旧 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 节点,如下图所示:

efd4244a-9f9c-11ee-8b88-92fbcf53809c.png

死循环执行步骤2

死循环的第二步操作是,线程 T2 时间片用完进入休眠状态,而线程 T1 开始执行扩容操作,一直到线程 T1 扩容完成后,线程 T2 才被唤醒,扩容之后的场景如下图所示:

efe63bd0-9f9c-11ee-8b88-92fbcf53809c.png

从上图可知线程 T1 执行之后,因为是头插法,所以 HashMap 的顺序已经发生了改变,但线程 T2 对于发生的一切是不可知的,所以它的指向元素依然没变,如上图展示的那样,T2 指向的是 A 元素,T2.next 指向的节点是 B 元素。

死循环执行步骤3

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

eff47b82-9f9c-11ee-8b88-92fbcf53809c.png

因为 T1 执行完扩容之后 B 节点的下一个节点是 A,而 T2 线程指向的首节点是 A,第二个节点是 B,这个顺序刚好和 T1 扩完容完之后的节点顺序是相反的。

T1 执行完之后的顺序是 B 到 A,而 T2 的顺序是 A 到 B,这样 A 节点和 B 节点就形成死循环了,这就是 HashMap 死循环导致的原因。

解决方案

HashMap 死循环的常用解决方案有以下 3 个:

使用线程安全容器 ConcurrentHashMap 替代(推荐使用此方案)。

使用线程安全容器 Hashtable 替代(性能低,不建议使用)。

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

总结

HashMap 死循环发生在 JDK 1.7 版本中,形成死循环的原因是 HashMap 在 JDK 1.7 使用的是头插法,头插法 + 链表 + 多线程并发 + HashMap 扩容,这几个点加在一起就形成了 HashMap 的死循环,解决死锁可以采用线程安全容器 ConcurrentHashMap 替代。







审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 多线程
    +关注

    关注

    0

    文章

    271

    浏览量

    19725
  • JDK
    JDK
    +关注

    关注

    0

    文章

    77

    浏览量

    16489
  • hashmap
    +关注

    关注

    0

    文章

    12

    浏览量

    2248

原文标题:阿里二面:为什么HashMap会产生死循环

文章出处:【微信号:CodeSheep,微信公众号:CodeSheep】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Hashmap实现原理20-多线程情况下扩容造成死循环#硬声创作季

    hashmap
    电子学习
    发布于 :2023年01月15日 12:04:49

    关于死循环语句

    do{..........} while(1) 和for(;;)[..............]这两个语句都代表死循环吧都是一样的意思吧!
    发表于 09-26 17:37

    如何避免Xil_Assert系列宏导致的死循环

      在调试模式下,Xil_Assert系列宏会调用Xil_Assert来检查参数是否正常。如果不正常,缺省情况下,没有打印,进入死循环。  通过调用void
    发表于 01-08 16:29

    为什么STM32仿真调试时会进入SystemInit死循环

    为什么STM32仿真调试时会进入SystemInit死循环?怎样去解决这个问题
    发表于 11-10 07:20

    怎么用C语言去写嵌入式系统的死循环

    怎么用C语言去写嵌入式系统的死循环?什么是ARM体系结构?ARM体系支持哪几种工作模式
    发表于 11-12 07:40

    外部中断函数初始化,执行的时候进入死循环是为什么?

    外部中断函数初始化,执行的时候进入死循环是为什么?
    发表于 11-25 08:10

    你怎么样用C语言编写死循环

    嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环?  一、while(1) { }没有划定初始化、更新区域的代码块(位置)。这两项代码的书写,就由作者来随意设置(完成)。后人接手程序,就要
    发表于 12-15 07:20

    死锁是什么?产生死锁的主要原因有哪些

    锁,就会造成系统死锁。产生死锁的三大主要原因:①系统资源不足②进程运行推进的顺序不合适③资源分配不当死锁的产生四个必要条件:①互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源。②环路等待条件(
    发表于 12-22 07:34

    如何去处理嵌入式软件产生死锁的情况

    嵌入式软件产生死锁的必要条件及原因有哪些?如何去处理嵌入式软件产生死锁的情况
    发表于 12-24 06:12

    怎么样用C语言去编写嵌入式系统中的死循环

    怎么样用C语言去编写嵌入式系统中的死循环?关键字volatile有什么含义吗?
    发表于 12-24 07:46

    RT-Thread如何去发现该线程已经进入异常的死循环

    各位大佬,假在某个线程的执行代码的异常执行流中,如else处理中写了while(1)这种死循环代码,rt-thread如何去发现该线程已经进入异常的死循环?求指教。
    发表于 10-18 09:57

    关于Java HashMap的认知

    HashMap详解 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet
    发表于 09-27 16:34 0次下载
    关于Java <b class='flag-5'>HashMap</b>的认知

    为什么单片机的主程序是死循环

    任何一个可用程序都必然是死循环程序,这不仅仅是指单片机程序。因为任何微处理器系统一旦开机,系统都在处理内部事件和外设响应,这个过程是一个循环过程,除非关机才能结束这个死循环程序。因此,对于单片机编程必须注意以下几点
    发表于 07-15 17:38 4809次阅读

    单片机的死循环有什么作用

    单片机是可编程器件,在使用时需要编写满足需求的程序。其C语言程序在各个端口、配置初始化完成后,会进入一个死循环,一般用while(1){;}的形式。初始化完成后,单片机就在死循环内一遍又一遍的执行程序逻辑。复位后,就从头开始,初始化完成后,再次进入
    发表于 08-09 17:01 5172次阅读
    单片机的<b class='flag-5'>死循环</b>有什么作用

    如何避免Xil_Assert系列宏导致的死循环的情况

    在调试模式下,Xil_Assert系列宏会调用Xil_Assert来检查参数是否正常。如果不正常,缺省情况下,没有打印,会进入死循环。 通过调用void Xil_AssertSetCallback
    的头像 发表于 12-02 16:20 3826次阅读
    如何避免Xil_Assert系列宏导致的<b class='flag-5'>死循环</b>的情况