11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

可莉
• 阅读 434

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

点击上方蓝字关注我!

jdk1.7 hashmap的循环依赖问题是面试经常被问到的问题,如何回答不好,可能会被扣分。今天我就带大家一下梳理一下,这个问题是如何产生的,以及如何解决这个问题。

一、hashmap的数据结构

先一起看看jdk1.7 hashmap的数据结构

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

数组 + 链表

hashmap会给每个元素的key生成一个hash值,然后根据这个hash值计算一个在数组中的位置i。i不同的元素放在数组的不同位置,i相同的元素放在链表上,最新的数据放在链表的头部。

往hashmap中保存元素会调用put方法,获取元素会调用get方法。接下来,我们重点看看put方法。

二、put方法

重点看看put方法

public V put(K key, V value) {

再看看addEntry方法

void addEntry(int hash, K key, V value, int bucketIndex) {

看看resize是如何扩容的

 void resize(int newCapacity) {

出问题的就是这个transfer方法

void transfer(Entry[] newTable, boolean rehash) {

我来给大家分析一下,为什么这几个代码是头插法,网上很多技术文章都没有说清楚。

三、头插法

我们把目光聚焦到这几行代码:

  //获取下一个元素,记录到一个临时变量,以便后面使用

假设刚开始hashMap有这些数据

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

调用put方法需要进行一次扩容,刚开始会创建一个空的数组,大小是以前的2倍,如图所示:

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

开始第一轮循环:

  //next= 7   e = 3  e.next = 7

执行完之后,第一轮循环之后数据变成这样的

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

再接着开始第二轮循环:

  //next= 5   e = 7  e.next = 5

上面会构成一个新链表,连接的顺序正好反过来了。

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

由于第二次循环时,节点key=7的元素插到相同位置上已有元素key=3的前面,所以说是采用的头插法。

四、死循环的产生

接下来重点看看死循环是如何产生的?

假设数据跟元素数据一致,有两个线程:线程1 和 线程2,同时执行put方法,最后同时调用transfer方法。

线程1 先执行,到  Entry<K,V> next = e.next; 这一行,被挂起了。

  //next= 7   e = 3  e.next = 7

此时线程1 创建的数组会创建一个空数组

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

接下来,线程2开始执行,由于线程2运气比较好,没有被中断过,执行完毕了。

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

过一会儿,线程1被恢复了,重新执行代码。

  //next= 7   e = 3  e.next = 7

这时候线程1的数组会变成这样的

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

再执行第二轮循环,此时的e=7

  //next= 3   e = 7  e.next = 3

这里特别要说明的是 此时e=7,而e.next为什么是3呢?

因为hashMap的数据是公共的,还记得线程2中的生成的数据吗?

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

此时e=7,那么e.next肯定是3。

经过上面第二轮循环之后,线程1得到的数据如下:

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

此时由于循环判断还没有退出,判断条件是: while(null != e),所以要开始第三轮循环:

  //next= null   e = 3  e.next = null

由于e=null,此时会退出循环,最终线程1的数据会是这种结构:

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

key:3 和 key:7又恢复了刚开始的顺序,但是他们的next会相互引用,构成环形引用。

注意,此时调用hashmap的get方法获取数据时,如果只是获取循环链上key:3 和 key:7的数据,是不会有问题的,因为可以找到。就怕获取循环链上没有的数据,比如:key:11,key:15等,会进入无限循环中导致CPU使用率飙升。

五、如何避免死循环

为了解决这个问题,jdk1.8把扩容是复制元素到新数组由 头插法 改成了 尾插法 。此外,引入了红黑树,提升遍历节点的效率。在这里我就不过多介绍了,如果有兴趣的朋友,可以关注我的公众号,后面会给大家详细分析jdk1.8的实现,以及 jdk1.7、jdk1.8 hashmap的区别。

此外,HashMap是非线程安全的,要避免在多线程的环境中使用HashMap,而应该改成使用ConcurrentHashMap。

所以总结一下要避免发生死循环的问题的方法:

  1. 升级jdk到1.8以上

  2. 改成ConcurrentHashMap

如果这篇文档对您有所帮助的话,麻烦关注一下我的公众账号:苏三说技术,或者帮忙点赞或转发,坚持原创不易,您的支持是我坚持最大的动力。后面我会分享更多更实用的干货,谢谢大家的支持。

11张图让你彻底明白jdk1.7 hashmap的死循环是如何产生的

本文分享自微信公众号 - 苏三说技术(gh_9f551dfec941)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
2021 最顶级 React 组件库推荐
点上方蓝字关注公众号「前端从进阶到入院」作者丨MaxRozen译者丨王强策划丨小智AntDesign!(https://oscimg.oschina.net/oscnet/a85c35f23bd04e5da6a1e5e68a24119b.png)项目链接:AntDesignh
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
可莉 可莉
2年前
2021 最顶级 React 组件库推荐
点上方蓝字关注公众号「前端从进阶到入院」作者丨MaxRozen译者丨王强策划丨小智AntDesign!(https://oscimg.oschina.net/oscnet/a85c35f23bd04e5da6a1e5e68a24119b.png)项目链接:AntDesignh
Wesley13 Wesley13
2年前
CSS 奇思妙想:超级酷炫的边框动画
点上方蓝字关注公众号「前端从进阶到入院」精选原创好文助你进入大厂文章转载自公众号「iCSS前端趣闻」今天逛博客网站shoptalkshow\1\,看到这样一个界面,非常有意思:!(https://oscimg.oschina.net/oscnet/9655b35af5a045999ff55c144a3f7c
Stella981 Stella981
2年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这