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

hashmap 死循环是一个比较常见、比较经典的问题,在日常的面试中出现的频率比较高,今天这篇文章咱们就通过图解的方式,带大家彻底梳理和理解一下这个问题。
前置知识
首先我们要了解一下为什么会有这个问题的发生?
死循环问题发生在 jdk 1.7 版本中,造成这个问题主要是由于 hashmap 自身的运行机制,加上并发操作,从而导致了死循环。在 jdk 1.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 开始执行扩容操作,一直到线程 t1 扩容完成后,线程 t2 才被唤醒,扩容之后的场景如下图所示:
从上图可知线程 t1 执行之后,因为是头插法,所以 hashmap 的顺序已经发生了改变,但线程 t2 对于发生的一切是不可知的,所以它的指向元素依然没变,如上图展示的那样,t2 指向的是 a 元素,t2.next 指向的节点是 b 元素。
死循环执行步骤3
当线程 t1 执行完,而线程 t2 恢复执行时,死循环就建立了,如下图所示:
因为 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 替代。


明年OLED面板市场份额有望首超TFT-LCD面板
说一说SVPWM发波到底是怎么回事
多户型检测电能表的原理及设计
BW Space智能追踪水下无人机,在Kickstarter平台发起众筹
水平对置发动机的优缺点
为什么HashMap会产生死循环呢?
荣耀8评测:华为荣耀8、酷比H9对比评测,颜值与双摄兼备你该怎么选?
直击丨吸睛时刻,绘王多款新品亮相高交会
AVX贴片钽电容的型号命名方式
OPPO Find X2 Neo评测:快速流畅的性能,Neo可以满足您的需求
openharmony开机是什么样的
源创通信SinoV-X508-8/32 GOIP语音网关介绍
华为Mate20Pro拍照成绩登顶
美国加快开发先进核反应堆设计
9月国内新能源汽车交付纷纷创新高;苹果供应商大立光:无刘海方案已交由客户……
视频监控系统的主要技术有哪些
划片机实现装片、对准、切割、清洗到卸片的自动化操作
食物中的直链淀粉和直链淀粉测定仪的介绍
虚拟运营商:熬至滴水成珠迎来商用
英国孕妇夜间手枕iPhone7入睡 疑似充电被烧伤拒绝更换手机