高并发下的 HashMap 为什么会死循环

5782656843ec1918b854abe5ba5561db.gif

作者 | tech-bus.七十一

来源 | 程序员巴士

前言

  HashMap并发情况下产生的死循环问题在JDK 1.7及之前版本是存在的,JDK 1.8 通过增加loHead头节点和loTail尾节点进行了修复,虽然进行了修复,但是如果涉及到并发情况下需要使用hash表,建议使用CurrentHashMap替代HashMap来确保不会出现线程安全问题。

  JDK 1.7及之前 HashMap在并发情况下产生的循环问题,将可能致使服务器的CPU飙升至100%,那究竟是如何造成的呢,为了解答这个疑惑,今天就带大家来了解一下线程不安全的HashMap在高并发的情况下是如何造成死循环的,要探究HashMap死循环的原因首先要从HashMap的源码开始进行分析,这样才能从根本上对HashMap进行理解,从而了解问题产生的原因。在分析之前我们要知道在JDK 1.7版本及之前HashMap采用的是数组 + 链表的数据结构,而在JDK 1.8则是采用数组 + 链表 + 红黑树的数据结构以进一步降低hash冲突后带来的查询损耗。

正文

一切还得从元素插入说起,HashMap进行元素的插入,这里会调用put()方法

public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);//分配数组空间}if (key == null)return putForNullKey(value);int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀int i = indexFor(hash, table.length);//获取在table中的实际位置for (Entry<K,V> e = table[i]; e != null; e = e.next) {...}modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败//重点关注这个addEntry增加元素的方法addEntry(hash, key, value, i);return null;}

紧接着我们来看这个addEntry()方法,里面调用的resize()扩容方法是今天的主角

void addEntry(int hash, K key, V value, int bucketIndex) {if ((size >= threshold) && (null != table[bucketIndex])) {resize(2 * table.length);//当size超过临界阈值threshold,并且即将发生哈希冲突时进行扩容,扩容后新容量为旧容量的2倍hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);//扩容后重新计算插入的位置下标}//把元素放入HashMap的桶的对应位置createEntry(hash, key, value, bucketIndex);}

下面我们进入到resize()法中,再揭开里面的transfer()方法的面纱,这个方法也是造成死循环的罪魁祸首

//按新的容量扩容Hash表  void resize(int newCapacity) {  Entry[] oldTable = table;//旧数据  int oldCapacity = oldTable.length;//获取旧的容量值  if (oldCapacity == MAXIMUM_CAPACITY) {//旧的容量值已经到了最大容量值  threshold = Integer.MAX_VALUE;//修改扩容阀值  return;  }  //新的结构  Entry[] newTable = new Entry[newCapacity];  //将老的表中的数据拷贝到新的结构中transfer(newTable, initHashSeedAsNeeded(newCapacity));table = newTable;//修改HashMap的底层数组  threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);//修改阀值  }

最后一起来仔细分析这个transfer()方法

//将老的表中的数据拷贝到新的结构中  void transfer(Entry[] newTable, boolean rehash) {  int newCapacity = newTable.length;//容量  for (Entry<K,V> e : table) { //遍历所有桶while(null != e) {  //遍历桶中所有元素(是一个链表)Entry<K,V> next = e.next;  if (rehash) {//如果是重新Hash,则需要重新计算hash值  e.hash = null == e.key ? 0 : hash(e.key);  }  int i = indexFor(e.hash, newCapacity);//定位Hash桶  e.next = newTable[i];//元素连接到桶中,这里相当于单链表的插入,总是插入在最前面newTable[i] = e;//newTable[i]的值总是最新插入的值e = next;//继续下一个元素  }  }  }

添加元素达到阀值后对HashMap进行扩容,走reaize()方法,在对HashMap进行扩容时,又会调用一个transfer()对旧的HashMap中的元素进行转移,那么我们今天要探究的死循环问题 就是发生在这个方法里的,在进行元素转移时transfer()方法里会调用下面四行代码

Entry<K,V> next = e.next; 
e.next = newTable[i];
newTable[i] = e;
e = next;

把元素插入新的HashMap中,粗略的看下这四行代码似乎并没有什么问题,元素进行转移的图如下(线程不冲突的情况下)

f6963fbdbde4a3153df536e965b7a666.png

那么我们让线程A、B同时访问我这段代码,当现A线程执行到以下代码时

8d5ff9dbdbe22222d0087c27e0c7963b.png

Entry<k,v> next = e.next;

线程A交出时间片,线程B这时候接手转移并且完成了元素的转移,这个时候线程A又拿到时间片并接着执行后续的代码

aa1720c5a671c6e25683daf5537aab96.png

执行后代码如图,当e = a时,这时候这时候再执行

e.next = newTable[i];// a元素指向了b元素,产生了循环

23db0361d93d3d667b60b5ed8e2e3225.png

  在链表产生了循环后,当get()方法获取元素的时候正好落在这个循环的链表上时,线程会一直在环里遍历,无法跳出,从而导致CPU飙升至100%!

总结

在多线程情况下尽量不要用HashMap,可以用线程安全的hash表来代替,例如使用下面这些

oncurrentHashMap、HashTable、Collections.synchronizedMap() 来避免产生多线程安全问题。

eec07fb824fe3019d466d3d09834342d.gif

3c6a026fb6b288088844249a4c5db482.png

往期推荐

云计算到底是谁发明的?

长跑11年,腾讯开源的变与不变

低代码发展专访系列之一:低代码平台产品的使用者都是谁?

内容整理志愿者招募了!

6942374115c7e2d886de9f822504e52a.gif

点分享

e58d3bad4d6b1b50ed5760e34a1d67eb.gif

点收藏

53676c9750b3d042ebb5a158159fd352.gif

点点赞

4b46a87cc36bbc85ea0d0e8803458b21.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/513143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

唯品会:在 Flink 容器化与平台化上的建设实践

简介&#xff1a; 唯品会 Flink 的容器化实践应用&#xff0c;Flink SQL 平台化建设&#xff0c;以及在实时数仓和实验平台上的应用案例。 转自dbaplus社群公众号 作者&#xff1a;王康&#xff0c;唯品会数据平台高级开发工程师 自 2017 年起&#xff0c;为保障内部业务在平…

python怎么变成exe_Python怎样打包成exe?

分类&#xff1a;Python &#xff5c; 作者&#xff1a;凹凸曼 &#xff5c; 发表于2011/03/01Python怎样打包成exe&#xff1f;已关闭评论 发现PyInstaller 是个不错的东东&#xff0c;解决打包单个exe的问题&#xff0c;使用非常简单&#xff0c;不用编写setup脚本&#xff1…

PolarDB-X 2.0:使用一个透明的分布式数据库是一种什么体验

简介&#xff1a; 透明分布式&#xff0c;是PolarDB-X即将发布的能力&#xff0c;它能让应用在使用PolarDB-X的过程中&#xff0c;犹如使用单机数据库一般的体验。与传统的中间件类型的“分布式数据库”相比&#xff0c;有了透明分布式能力的PolarDB-X&#xff0c;不再需要应用…

Chrome 96 又更新了 5 个巨巨巨好用的功能

作者 | 零一来源 | 前端印象‍‍‍‍‍‍‍大家好&#xff0c;收到了 Chrome 96 版本的更新推送&#xff0c;简单看了一下&#xff0c;还是更新了几个挺有趣的东西的&#xff0c;一起来看看到底都有啥~先下载 Chrome Beta 版本才能体验 Chrome 96 哈Chrome Beta我们顺便来给每个…

编译优化 | LLVM代码生成技术详解及在数据库中的应用

简介&#xff1a; 作者&#xff1a;长别 1. 前言 随着IT基础设施的发展&#xff0c;现代的数据处理系统需要处理更多的数据、支持更为复杂的算法。数据量的增长和算法的复杂化&#xff0c;为数据分析系统带来了严峻的性能挑战。近年来&#xff0c;我们可以在数据库、大数据系…

低代码发展专访系列之二:两三年内会出现“现象级”低代码产品吗?

前言&#xff1a;2019年开始&#xff0c;低代码爆火。有人认为它是第四代编程语言&#xff0c;有人认为它是开发模式的颠覆&#xff0c;也有人认为是企业管理模式的变革……有很多声音&#xff0c;社区讨论很热烈。CSDN 随后展开低代码平台产品系列活动&#xff0c;包括低代码开…

为什么Spring仍然会是云原生时代最佳平台之一?

简介&#xff1a; 基于Java语言的Spring生态&#xff0c;还能否适应新的开发方式&#xff0c;比如Cloud Native、Serverless、Faas等&#xff0c;它还会是云原生时代的最佳平台的选择吗&#xff1f;本文将从5个角度来为你分析一下这个问题&#xff0c;分别是&#xff1a;Java和…

贾又福大象鸿蒙,奏乐!继续吹!库里又创记录,射进MVP榜单,众多名记变“库吹“...

库里本月已投进85记三分 打破哈登保持的NBA单月三分命中数纪录加上今天的7记三分&#xff0c;库里本月已经投进85记三分&#xff0c;创造了新的NBA单月(自然月)三分命中数纪录。勇士本月还有两场比赛。此前&#xff0c;哈登曾单月82记三分。在NBA历史单月三分球命中数前三榜单中…

opencv4 图像特征匹配_概述 | 全景图像拼接技术全解析

点击上方蓝字关注我们微信公众号&#xff1a;OpenCV学堂关注获取更多计算机视觉与深度学习知识前言图像/视频拼接的主要目的是为了解决相机视野(FOV-Field Of View)限制&#xff0c;生成更宽的FOV图像/视频场景。视频拼接在体育直播、全景显示、数字娱乐、视频处理中都被广泛应…

数字化让618有了洞悉消费者内心的“大脑”

简介&#xff1a; 阿里云数据中台已形成包括会员智能运营、全域天攻智投、GMV策略模拟等在内的近10套解决方案&#xff0c;围绕“人”“货”“场”三大零售行业要素&#xff0c;逐个击破品牌业务难点&#xff0c;记者了解到&#xff0c;过去一年&#xff0c;悦诗风吟、Benefit、…

赋能工业互联网融合发展 | 北京信息化和工业化融合服务联盟平台化设计专业委员会、中国仿真学会CAE仿真专业委员会成立

11月28日&#xff0c;由北京市经济和信息化局指导&#xff0c;北京信息化和工业化融合服务联盟与中国仿真学会共同主办&#xff0c;联盟平台化设计专业委员会、中国仿真学会CAE仿真专业委员会、国家数字化设计与制造创新中心北京中心、北京数字化设计与制造产业创新中心共同承办…

升级鸿蒙系统有没有翻车,被寄予厚望的华为鸿蒙系统,这次要翻车?原来并不是我们想的那样...

华为鸿蒙系统早在去年就已经被正式发布&#xff0c;但那时的人们对这个操作系统还不熟悉。但近期华为又在其发布会上发布了鸿蒙OS2.0&#xff0c;并表示到了2021年华为手机将全面使用鸿蒙2.0。这消息一出&#xff0c;不少华为用户忍不住想去尝尝鲜&#xff0c;纷纷都将系统更新…

PolarDB-X 2.0 全局 Binlog 和备份恢复能力解读

简介&#xff1a; PolarDB-X 2.0 针对数据孤岛问题提供了全局 Binlog 能力&#xff0c;该能力为下游生态提供了与 MySQL Binlog 完全一致的增量日志消费体验。针对数据损坏问题提供了实例级、表级、SQL 级和行级等不同粒度的数据恢复能力&#xff0c;包括一致性备份恢复、表回收…

友盟+《小程序用户增长白皮书》:从五个角度入手分析小程序数据

简介&#xff1a; 近日&#xff0c;国内领先的全域数据智能服务商——友盟&#xff0c;发布了《友盟U-APM 移动应用性能体验报告》。据悉&#xff0c;友盟于去年将原移动分析U-App错误分析模块正式升级为U-APM应用性能监控平台&#xff0c;经过近一年的观察&#xff0c;通过DEM…

html提现页面模板,提现记录.html

&#xfeff;提现记录$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.html; };…

有赞九周年,打造技术生态,与开发者一起投身新零售浪潮

编辑 | 宋慧 11月28日&#xff0c;在有赞九周年生态大会有赞云分会场上&#xff0c;有赞宣布全面升级“ONE战略”&#xff0c;将与生态内众多的品牌商、软件厂商&#xff0c;从“产品融合”&#xff0c;“销售联动”&#xff0c;“经验共享”和“资本合作”四个维度实一起共建“…

“控本焦虑”的工程企业 用钉钉宜搭找到了低成本数字化的“捷径”

简介&#xff1a; 上海致拓软件有限公司利用云钉低代码应用构建平台——钉钉宜搭为合安建筑快速、低成本地搭建了个性化的项目管理系统&#xff0c;着力帮助合安建筑解决业务在线场景&#xff0c;形成场景化的工程项目管理数字化解决方案。 一封由工程公司发给项目管理数字化实…

如何做好一场技术演讲?

简介&#xff1a; 据心理学调查&#xff0c;在人们感到最恐惧的事情里&#xff0c;死亡排名第二&#xff0c;而“公开演讲”排名第一&#xff01;那么作为一个演讲新人&#xff0c;为了可以不丢人的做好演讲&#xff0c;都需要做哪些准备呢&#xff1f; 作者 | 竹涧 来源 | 阿里…

汇聚技术与能力,共绘区块链远大蓝图!

进入数字经济时代&#xff0c;云已成为数字经济的一个最重要的基础设施。区块链&#xff0c;作为跨产业数字生态的连接器,是数字经济时代另一个重要的基础设施。云链结合&#xff0c;让技术助力传统产业升级&#xff0c;重塑信任关系。11月30日&#xff0c;移动云区块链开发者论…

python代码300行程序_python小工具,15行代码秒出工资条

公司工资条经常使用Excel制作&#xff0c;但是每个月都要做一遍&#xff0c;能不能用python写个程序自动化完成这想工作&#xff1f;当然可以&#xff0c;而且只是分分钟的事&#xff01; 先来看看原始数据是什么样子&#xff1a; 最后做成的效果:使用Excel每次都需要手动修改一…