我的编程空间,编程开发者的网络收藏夹
学习永远不晚

HashMap在高并发下会出现什么问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

HashMap在高并发下会出现什么问题

本篇内容介绍了“HashMap在高并发下会出现什么问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

众所周知,HashMap不是线程安全的,在高并发情况下会出现问题。特别是,在java1.7中,多线程的HashMap会出现CPU 100%的严重问题。这个问题是怎样产生的,后续版本还会有这个问题吗(指java8及后续版本)?下面就来用通俗的语言讲解下。

解析

关于这个问题,是由于java7多线程扩容机制下链表变为循环链表,再获取该链表导致的。

看下java7中扩容的代码。java7中HashMap的实现为数组+链表的形式,没有红黑树。

java7扩容的原则很简单,新数组长度为原数组2倍。遍历原数组,将数组每个位置(有可能为空,有可能只有一个数组,有可能是一个链表)重新哈希,放到对应的新数组上。全部遍历完后更改数组指针,指向新数组。需要注意的是,这里重哈希将链表元素放到新数组,使用的是头插法。

 // 扩容核心方法,基本思想就是遍历数据,使用头插法将旧数组元素移到新数组。 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) {                    e.hash = null == e.key ? 0 : hash(e.key);                }                int i = indexFor(e.hash, newCapacity);                e.next = newTable[i]; // 头插法,新节点next指向该位置首节点                newTable[i] = e; // 新元素归位                e = next; // 指向下一个节点,继续遍历            }        }    }   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;  // 更改指针        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);    }

这里处理的话,如果单线程情况下不会有问题。如果在多线程情况下,会导致链表在扩容过程中形成循环链表。

形成循环链表的原因在于多线程和头插法。试想,两个线程在添加元素时,同时发现该扩容了,然后同时发起扩容过程。由上述代码可知,扩容完成之前是在自己的线程里创建一个新数组。等扩容完成后(也就是将原数组元素迁移到新数组后)再更改指针指向新扩容数组。

举例初始HashMap是这样的

HashMap在高并发下会出现什么问题

假设两个线程同时扩容,一个线程扩容到一半后被挂起。(标识了某链表的e和next),另一个线程执行扩容,且完成了扩容。

HashMap在高并发下会出现什么问题

红色的数组和元素表示线程1,也就是扩容一半挂起的线程,而线程二已完成扩容。观察完成扩容的线程二,在3的位置,该链表的位置顺序已经改变(原数组顺讯为3->7,现在反过来了,这是使用头插法的效果,你也可以对着代码试试)。从图中也可以看出,线程1,2分别创建了自己的新数组,并在自己的新数组中完成扩容。

这时线程1开始执行。熟悉下它即将执行的代码。

   // transfer 方法循环部分         while(null != e) {                Entry<K,V> next = e.next;                if (rehash) {                    e.hash = null == e.key ? 0 : hash(e.key);                }                int i = indexFor(e.hash, newCapacity);                e.next = newTable[i]; // 头插法,新节点next指向该位置首节点                newTable[i] = e; // 新元素归位                e = next; // 指向下一个节点,继续遍历            }

下面线程1将使用头插法将元素插入线程1新建的数组中去。注意此时e指向的是Key3,next指向的是Key7。不用想也知道后面操作会有问题。因为现在的next指针指的不是e的下一个元素,而是它的前一个元素!

如果继续走代码的话,把Key3(当前e指向元素)放入新数组后,再把Key7放入新数组,后面会放哪个元素?当然又是Key3了,因为Key7next是Key3,这样就形成了死循环。

HashMap在高并发下会出现什么问题

java8的改进

  • 添加了红黑树,当链表长度大于8时,会将链表转为红黑树。

  • 扩容后,新数组中的链表顺序依然与旧数组中的链表顺序保持一致。具体JDK8是用 head 和 tail 来保证链表的顺序和之前一样,这样就不会产生循环引用。也就没有死循环了。

  • 虽然修复了死循环的BUG,但是HashMap 还是非线程安全类,仍然会产生数据丢失等问题。

“HashMap在高并发下会出现什么问题”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

HashMap在高并发下会出现什么问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

HashMap在高并发下会出现什么问题

本篇内容介绍了“HashMap在高并发下会出现什么问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言众所周知,HashMap不是线程安全
2023-06-20

主板有问题会出现什么现象

这篇文章给大家分享的是有关主板有问题会出现什么现象的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。主板有问题会出现的现象:1、开机无显示;2、CMOS设置不能保存;3、安装主板驱动程序后出现死机或光驱读盘速度变慢;
2023-06-14

Golang为何在高并发情境下表现出色?

Golang为何在高并发情境下表现出色?随着互联网应用的普及和数据量的不断增加,高并发场景下的稳定性和性能成为了开发者们关注的重点。在这种情况下,Golang(Go)这门编程语言备受瞩目,因为它在高并发情境下表现出色。本文将从Golang
Golang为何在高并发情境下表现出色?
2024-02-29

java高并发下脏读问题怎么解决

在Java高并发下解决脏读问题可以通过使用锁机制或者使用线程安全的数据结构来实现。1. 使用锁机制:可以使用synchronized关键字或者Lock接口的实现类来对共享数据进行加锁,确保在一个线程读取数据时,其他线程无法修改该数据,从而避
2023-08-23

Tomcat 9.0.26高并发场景下DeadLock问题怎么处理

这篇文章给大家分享的是有关Tomcat 9.0.26高并发场景下DeadLock问题怎么处理的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、Tomcat容器 9.0.26 版本 Deadlock 问题1.1 问
2023-06-04

golang出现内存逃逸会导致什么问题

当Golang程序出现内存逃逸时,会导致以下几个问题:垃圾回收开销增加:内存逃逸会导致对象分配在堆上,而不是在栈上。这意味着垃圾回收器需要更频繁地扫描堆内存,增加了垃圾回收的开销。性能下降:由于内存逃逸会导致对象在堆上分配,而堆上的内存分配
2023-10-26

腾讯云服务器为什么会出现卡顿问题?

1.资源配置不足腾讯云服务器的性能与所选的配置密切相关。如果你选择的配置不足以支撑你的应用程序或网站的需求,就容易出现卡顿问题。在购买服务器时,需要根据实际需求选择适当的配置,包括CPU、内存、存储和带宽等。2.网络延迟网络延迟是指数据在传输过程中的延迟时间。如果你的服务器与用户之间的网络连接存在延迟,就会导致用户访问
2023-10-27

腾讯云服务器:为什么会出现卡顿问题?

1.硬件配置不足腾讯云服务器的性能与硬件配置直接相关。如果你选择的服务器配置较低,例如CPU、内存或存储空间不足,那么在处理大量请求或运行复杂应用程序时,服务器可能会出现卡顿现象。2.网络延迟网络延迟是另一个可能导致腾讯云服务器卡顿的因素。如果你的服务器与用户之间的网络连接不稳定或带宽不足,那么用户访问你的网站或应用程
2023-10-27

Android开发中什么情况下会出现内存泄漏

今天就跟大家聊聊有关Android开发中什么情况下会出现内存泄漏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 android中一个对象已经不需要了,但是其他对象还持有他的引用,导致
2023-05-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录