漫画:高并发下的HashMap

转载自 玻璃猫 程序员小灰

上一期我们介绍了HashMap的基本原理,

这一期我们来讲解高并发环境下,HashMap可能出现的致命问题。













HashMap的容量是有限的。当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高。


这时候,HashMap需要扩展它的长度,也就是进行Resize





影响发生Resize的因素有两个:


1.Capacity

HashMap的当前长度。上一期曾经说过,HashMap的长度是2的幂。


2.LoadFactor

HashMap负载因子,默认值为0.75f。



衡量HashMap是否进行Resize的条件如下:


HashMap.Size   >=  Capacity * LoadFactor







1.扩容

创建一个新的Entry空数组,长度是原数组的2倍。


2.ReHash

遍历原Entry数组,把所有的Entry重新Hash到新数组。为什么要重新Hash呢?因为长度扩大以后,Hash的规则也随之改变。


让我们回顾一下Hash公式:

index =  HashCode(Key) &  (Length - 1) 


当原数组长度为8时,Hash运算是和111B做与运算;新数组长度为16,Hash运算是和1111B做与运算。Hash结果显然不同。


Resize前的HashMap:




Resize后的HashMap:




ReHash的Java代码如下:


[java] view plaincopy
  1. /** 
  2.  * Transfers all entries from current table to newTable. 
  3.  */  
  4. void transfer(Entry[] newTable, boolean rehash) {  
  5.     int newCapacity = newTable.length;  
  6.     for (Entry<K,V> e : table) {  
  7.         while(null != e) {  
  8.             Entry<K,V> next = e.next;  
  9.             if (rehash) {  
  10.                 e.hash = null == e.key ? 0 : hash(e.key);  
  11.             }  
  12.             int i = indexFor(e.hash, newCapacity);  
  13.             e.next = newTable[i];  
  14.             newTable[i] = e;  
  15.             e = next;  
  16.         }  
  17.     }  
  18. }  








注意:下面的内容十分烧脑,请小伙伴们坐稳扶好。


假设一个HashMap已经到了Resize的临界点。此时有两个线程A和B,在同一时刻对HashMap进行Put操作:







此时达到Resize条件,两个线程各自进行Rezie的第一步,也就是扩容:




这时候,两个线程都走到了ReHash的步骤。让我们回顾一下ReHash的代码:



假如此时线程B遍历到Entry3对象,刚执行完红框里的这行代码,线程就被挂起。对于线程B来说:


e = Entry3

next = Entry2


这时候线程A畅通无阻地进行着Rehash,当ReHash完成后,结果如下(图中的e和next,代表线程B的两个引用):




直到这一步,看起来没什么毛病。接下来线程B恢复,继续执行属于它自己的ReHash。线程B刚才的状态是:

e = Entry3

next = Entry2

当执行到上面这一行时,显然 i = 3,因为刚才线程A对于Entry3的hash结果也是3。



我们继续执行到这两行,Entry3放入了线程B的数组下标为3的位置,并且e指向了Entry2。此时e和next的指向如下:


e = Entry2

next = Entry2


整体情况如图所示:





接着是新一轮循环,又执行到红框内的代码行:



e = Entry2

next = Entry3


整体情况如图所示:




接下来执行下面的三行,用头插法把Entry2插入到了线程B的数组的头结点:



整体情况如图所示:




第三次循环开始,又执行到红框的代码:



e = Entry3

next = Entry3.next = null


最后一步,当我们执行下面这一行的时候,见证奇迹的时刻来临了:


newTable[i] = Entry2

e = Entry3

Entry2.next = Entry3

Entry3.next = Entry2


链表出现了环形!


整体情况如图所示:





此时,问题还没有直接产生。当调用Get查找一个不存在的Key,而这个Key的Hash结果恰好等于3的时候,由于位置3带有环形链表,所以程序将会进入死循环











1.Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是

HashMap.Size   >=  Capacity * LoadFactor。


2.Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。


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

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

相关文章

jvm(11)-晚期(运行期)优化

【0】README 0.1&#xff09;本文部分文字描述转自 “深入理解 jvm”&#xff0c;旨在学习 晚期&#xff08;运行期&#xff09;优化 的基础知识&#xff1b; 【1】概述 1&#xff09;即时编译器&#xff08;JITjust in time compiler&#xff09;定义&#xff1a;为了提高…

java 新功能_Java 14的新功能

java 新功能2020年3月17日&#xff0c;Oracle发布了名为Java 14的Java新版本&#xff0c;其中包括许多新功能&#xff0c;工具&#xff0c;安全性&#xff0c;调试和更新的文档方面的改进。 但是&#xff0c;Oracle还向您提供Java的较旧版本&#xff0c;因为它具有向后兼容性&a…

漫画:什么是ConcurrentHashMap

转载自 玻璃猫 程序员小灰————————————————————————前两期我们讲解了HashMap的基本原理&#xff0c;以及高并发场景下存在的问题。没看过的小伙伴可以点击下面链接&#xff1a;漫画&#xff1a;什么是HashMap&#xff1f; 漫画&#xff1a;高并发下的H…

jvm(12)-java内存模型与线程

【0】README0.1&#xff09;本文部分文字描述转自“深入理解jvm”&#xff0c;旨在学习“java内存模型与线程” 的基础知识&#xff1b;【1】概述1&#xff09;并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计算机性能发展源动力的根本原因&#xff1b;2&#xff09;A…

junit mockito_从工作中清除代码–使用JUnit 5,Mockito和AssertJ编写可执行规范

junit mockito可执行规范是也可以用作设计规范的测试。 通过启用通用语言&#xff08;在DDD世界中&#xff0c;这也称为无处不在的语言 &#xff09;&#xff0c;它们使技术和业务团队能够进入同一页面。 它们充当代码的未来维护者的文档。 在本文中&#xff0c;我们将看到一种…

漫画:什么是红黑树

转载自 玻璃猫 程序员小灰————————————二叉查找树&#xff08;BST&#xff09;具备什么特性呢&#xff1f;1.左子树上所有结点的值均小于或等于它的根结点的值。 2.右子树上所有结点的值均大于或等于它的根结点的值。 3.左、右子树也分别为二叉排序树。下图中这棵树…

jvm(13)-线程安全与锁优化

【0】README 0.1&#xff09;本文部分文字转自“深入理解jvm”&#xff0c; 旨在学习 线程安全与锁优化 的基础知识&#xff1b; 0.2&#xff09;本文知识对于理解 java并发编程非常有用&#xff0c;个人觉得&#xff0c;所以我总结的很详细&#xff1b; 【1】概述 【2】线程安…

apache ignite_Kubernetes集群上的Apache Ignite和Spring第3部分:测试应用程序

apache ignite在上一个博客中&#xff0c;我们为Ignite应用程序创建了Kubernetes部署文件。 在此博客上&#xff0c;我们将在Kubernetes上部署Ignite应用程序。 我将在此使用minikube。 让我们先构建 mvn clean install 我将创建一个简单的Docker映像&#xff0c;因此需要Doc…

什么是AES算法?(整合版)

转载自 玻璃猫 程序员小灰 假设有一个发送方在向接收方发送消息。如果没有任何加密算法&#xff0c;接收方发送的是一个明文消息&#xff1a;“我是小灰” 如果消息被中间人截获到&#xff0c;即使中间人无法篡改消息&#xff0c;也可以窥探到消息的内容&#xff0c;从而暴露了…

soapui 测试soap_使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第二部分

soapui 测试soap在本系列的第一篇文章中&#xff0c;我们创建了一个基本的身份验证服务&#xff0c;以使用SoapUI进行调用。 因此&#xff0c;在第二篇文章中&#xff0c;我们将逐步演示如何使用此工具成功调用这种服务。 使用SoapUI的1-Basic WCF SOAP –创建新的SOAP项目 首…

如何获得即时编译器(JIT)的汇编代码(linux环境下)

【0】README0.1&#xff09;本文主要解决如何在linux下获取即时编译器的汇编代码问题&#xff1b;0.2&#xff09;本文部分内容转自&#xff1a;http://psy-lob-saw.blogspot.jp/2013/01/java-print-assembly.html1&#xff09;给定java源代码// 单例模式&#xff08;分析volat…

漫画:什么是SHA系列算法

转载自 玻璃猫 程序员小灰 SHA-1 SHA-1算法可以从明文生成160bit的信息摘要&#xff0c;示例如下&#xff1a; 给定明文&#xff1a;abcd SHA-1摘要&#xff1a;81FE8BFE87576C3ECB22426F8E57847382917ACF SHA-1 与 MD5的主要区别是什么呢&#xff1f; 1.摘要长度不同。 …

apache ignite_Kubernetes集群上的Apache Ignite和Spring第1部分:Spring Boot应用程序

apache ignite在之前的一系列博客中&#xff0c;我们在Kubernetes集群上启动了一个Ignite集群。 在本教程中&#xff0c;我们将使用先前在Spring Boot Application上创建的Ignite集群。 让我们使用Spring Boot创建我们的项目。 Spring Boot应用程序将连接到Ignite集群。 让我…

tomcat(1)一个简单的web server

【0】README 0.1&#xff09;本文部分描述转自“深入剖析tomcat”&#xff0c; 旨在学习 一个简单的web server 的基础知识&#xff1b; 0.2&#xff09;for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter1 【1】…

漫画:什么是MD5算法

转载自 玻璃猫 程序员小灰 摘要哈希生成的正确姿势是什么样呢&#xff1f;分三步&#xff1a; 1.收集相关业务参数&#xff0c;在这里是金额和目标账户。当然&#xff0c;实际应用中的参数肯定比这多得多&#xff0c;这里只是做了简化。 2.按照规则&#xff0c;把参数名和参数…

idea快速生成crud_Java / Spring:如何快速生成完整的Swagger文档CRUD REST API

idea快速生成crud作为开发人员&#xff0c;我们在日常生活中经常面临的最繁琐的任务之一就是编写良好且易于理解的文档。 无论我们的文档只有几行来解释功能的核心功能&#xff0c;还是表明系统的来龙去脉的成熟文章都没关系。 重要的是&#xff0c;我们试图通过文档传达的信息…

漫画:如何破解MD5算法

转载自 玻璃猫 程序员小灰 在之前的漫画中&#xff0c;我们介绍了MD5算法的基本概念和底层原理&#xff0c;没看过的小伙伴们可以点击下面的链接&#xff1a;《漫画&#xff1a;什么是MD5算法&#xff1f;》 这一次&#xff0c;我们来讲解如何破解MD5算法。 设MD5的哈希函数是…

自定义类加载器(ClassLoader + URLClassLoader)

【0】README 0.1&#xff09;本文主要对类加载器进行分析&#xff0c;且 URLClassLoader是 ClassLoader的子类&#xff1b; 0.2&#xff09;关于如何设置类加载器的加载路径&#xff0c;参见 对servlet容器的补充 【1】URLClassLoader类加载器 1.1&#xff09;URLClassLoad…

fork/join和线程池_从fork-join /线程池调用的Singelton bean中的访问spring请求范围缓存...

fork/join和线程池问题&#xff1a; 启用了Spring且其范围设置为Request的缓存需要由不在请求范围内的singleton bean访问。 解&#xff1a; Spring使您能够创建缓存&#xff0c;该缓存为请求范围保留数据。 例如 import org.springframework.cache.concurrent.ConcurrentMapC…

tomcat(2)一个简单的servlet容器

【0】README 0.1&#xff09;本文部分文字转自 “深入剖析Tomcat”&#xff0c;旨在学习 一个简单的servlet容器 的基础知识&#xff1b; 0.2&#xff09;for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter2 0…