Java中的HashMap为什么会产生死循环

目录
  • 前置知识
  • 死循环执行步骤1
  • 死循环执行步骤2
  • 死循环执行步骤3
  • 解决方案
  • 总结

前言:

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 替代。

到此这篇关于Java中的HashMap为什么会产生死循环的文章就介绍到这了,更多相关HashMap死循环内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于Java HashMap的死循环的启示详解

    一.单线程改造为多线程也是个技术活 正如我们看到耗子叔叔博客里写的那样,原来是单线程的应用程序,"后来,我们的程序性能有问题,所以需要变成多线程的,于是,变成多线程后到了线上,发现程序经常占了100%的CPU". 考虑到是淘宝的工程师曝出来的问题,他们的技术基础一般都很扎实,连他们都用错了,所以把单线程改造为多线程并不是想象中的那么简单,我认为. 你可能很不服气地反问,淘宝的工程师又怎么了,单线程改为多线程有什么难的?无非就是应用现有的多线程技术嘛,你看,我有非常强烈的线程安全意识,我

  • 深入了解JAVA HASHMAP的死循环

    前言 在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环.这个事情我4.5年前也经历过,本来觉得没什么好写的,因为Java的HashMap是非线程安全的,所以在并发下必然出现问题.但是,我发现近几年,很多人都经历过这个事(在网上查"HashMap Infinite Loop"可以看到很多人都在说这个事)所以,觉得这个是个普遍问题,需要写篇疫苗文章说一下这

  • Java中的HashMap为什么会产生死循环

    目录 前置知识 死循环执行步骤1 死循环执行步骤2 死循环执行步骤3 解决方案 总结 前言: HashMap 死循环是一个比较常见.比较经典的问题,在日常的面试中出现的频率比较高,所以接下来咱们通过图解的方式,带大家彻底理解死循环的原因. 前置知识 死循环问题发生在 JDK 1.7 版本中,造成这个问题主要是由于 HashMap 自身的运行机制,加上并发操作,从而导致了死循环. 在 JDK 1.7 中 HashMap 的底层数据实现是数组 + 链表的方式, 如下图所示:   而 HashMap

  • Java 中的HashMap详解和使用示例_动力节点Java学院整理

    第1部分 HashMap介绍 HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,HashMap中的映射不是有序的. HashMap 的实例有两个参数影响其性能:"初始容量" 和 "加载因子&quo

  • Java中使用HashMap改进查找性能的步骤

    Java中,HashMap,其实就是键值对.一个Key,对应一个值:写数据时,指定Key写对应值:读取时凭Key找到相应值.感觉就跟Redis差不多. // 创建 HashMap 对象 Sites HashMap<Integer, String> Sites = new HashMap<Integer, String>(); // 添加键值对 Sites.put(1, "Google"); Sites.put(2, "Runoob"); Si

  • 深入理解Java中的HashMap

    一.HashMap的结构图示 ​本文主要说的是jdk1.8版本中的实现.而1.8中HashMap是数组+链表+红黑树实现的,大概如下图所示.后面还是主要介绍Hash Map中主要的一些成员以及方法原理. ​那么上述图示中的结点Node具体类型是什么,源码如下.Node是HashMap的内部类,实现了Map.Entery接口,主要就是存放我们put方法所添加的元素.其中的next就表示这可以构成一个单向链表,这主要是通过链地址法解决发生hash冲突问题.而当桶中的元素个数超过阈值的时候就换转为红黑

  • java 中的HashMap的底层实现和元素添加流程

    目录 HashMap 底层实现 HashMap 插入流程 为什么要将链表转红黑树? 哈希算法实现 总结 前言: HashMap 是使用频率最高的数据类型之一,同时也是面试必问的问题之一,尤其是它的底层实现原理,既是常见的面试题又是理解 HashMap 的基石,所以重要程度不言而喻. HashMap 底层实现 HashMap 在 JDK 1.7 和 JDK 1.8 的底层实现是不一样的,在 JDK 1.7 中,HashMap 使用的是数组 + 链表实现的,而 JDK 1.8 中使用的是数组 + 链

  • 全面解析Java中的HashMap类

    HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算法决定集合元素

  • 深入理解Java中的HashMap的实现机制

    如果任何人让我描述一下HashMap的工作机制的话,我就简单的回答:"基于Hash的规则".这句话非常简单,但是要理解这句话之前,首先我们得了解什么是哈希,不是么? 什么是哈希 哈希简单的说就是对变量/对象的属性应用某种算法后得到的一个唯一的串,用这个串来确定变量/对象的唯一性.一个正确的哈希函数必须遵守这个准则. 当哈希函数应用在相同的对象或者equal的对象的时候,每次执行都应该返回相同的值.换句话说,两个相等的对象应该有相同的hashcode. 注:所有Java对象都从Objec

  • Java中一个for语句导致无穷大死循环的例子

    在Java开发中常用到For循环,它对简化业务处理,提高效率,非常有帮助.但要防止程序算法中可能导致死循环的情况,而且有的死循环还不好察觉.比如下面这个例子,算法极容易认为是50,实际上是无穷大的一个死循环. public class CycTest { /** * @param args the command line arguments */ public static void main(String[] args) { int end = Integer.MAX_VALUE; //定义

  • Java中对HashMap的深度分析

    在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键.由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题.找遍了大大小小的论坛,也把<Java 虚拟机规范>,<apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector>,和<Thinking in Java>翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感

  • Java中map内部存储方式解析

    Map,即映射,也称为 键值对,有一个 Key, 一个 Value . 比如 Groovy 语言中,  def  map = ['name' : 'liudehua', 'age' : 50 ] ,则 map[ 'name' ]  的值是 'liudehua'. 那么 Map 内部存储是怎么实现的呢?   下面慢慢讲解. 一. 使用 拉链式存储 这个以 Java 中的 HashMap 为例进行讲解.   HashMap 的内部有个数组 Entry[]  table, 这个数组就是存放数据的. E

随机推荐