漫画:什么是ConcurrentHashMap

转载自 玻璃猫 程序员小灰









————————————



























————————————







前两期我们讲解了HashMap的基本原理,以及高并发场景下存在的问题。没看过的小伙伴可以点击下面链接:


漫画:什么是HashMap?

漫画:高并发下的HashMap


如果实在懒得看也没有关系,我们来简单回顾一下HashMap的结构:




简单来说,HashMap是一个Entry对象的数组。数组中的每一个Entry元素,又是一个链表的头节点。


Hashmap不是线程安全的。在高并发环境下做插入操作,有可能出现下面的环形链表:


















Segment是什么呢?Segment本身就相当于一个HashMap对象。


同HashMap一样,Segment包含一个HashEntry数组,数组中的每一个HashEntry既是一个键值对,也是一个链表的头节点。


单一的Segment结构如下:




像这样的Segment对象,在ConcurrentHashMap集合中有多少个呢?有2的N次方个,共同保存在一个名为segments的数组当中。


因此整个ConcurrentHashMap的结构如下:





可以说,ConcurrentHashMap是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。


这样的二级结构,和数据库的水平拆分有些相似。









Case1:不同Segment的并发写入





不同Segment的写入是可以并发执行的。



Case2:同一Segment的一写一读





同一Segment的写和读是可以并发执行的。



Case3:同一Segment的并发写入





Segment的写入是需要上锁的,因此对同一Segment的并发写入会被阻塞。



由此可见,ConcurrentHashMap当中每个Segment各自持有一把锁。在保证线程安全的同时降低了锁的粒度,让并发操作效率更高。







Get方法:


1.为输入的Key做Hash运算,得到hash值。


2.通过hash值,定位到对应的Segment对象


3.再次通过hash值,定位到Segment当中数组的具体位置。



Put方法:


1.为输入的Key做Hash运算,得到hash值。


2.通过hash值,定位到对应的Segment对象


3.获取可重入锁


4.再次通过hash值,定位到Segment当中数组的具体位置。


5.插入或覆盖HashEntry对象。


6.释放锁。







Size方法的目的是统计ConcurrentHashMap的总元素数量, 自然需要把各个Segment内部的元素数量汇总起来。


但是,如果在统计Segment元素数量的过程中,已统计过的Segment瞬间插入新的元素,这时候该怎么办呢?











ConcurrentHashMap的Size方法是一个嵌套循环,大体逻辑如下:


1.遍历所有的Segment。


2.把Segment的元素数量累加起来。


3.把Segment的修改次数累加起来。


4.判断所有Segment的总修改次数是否大于上一次的总修改次数。如果大于,说明统计过程中有修改,重新统计,尝试次数+1;如果不是。说明没有修改,统计结束。


5.如果尝试次数超过阈值,则对每一个Segment加锁,再重新统计。


6.再次判断所有Segment的总修改次数是否大于上一次的总修改次数。由于已经加锁,次数一定和上次相等。


7.释放锁,统计结束。



官方源代码如下:


public int size() {// Try a few times to get accurate count. On failure due to
   // continuous async changes in table, resort to locking.
   final Segment<K,V>[] segments = this.segments;int size;boolean overflow; // true if size overflows 32 bits
    long sum;         // sum of modCounts
    long last = 0L;   // previous sum
    int retries = -1; // first iteration isn't retry
    try {for (;;) {if (retries++ == RETRIES_BEFORE_LOCK) {for (int j = 0; j < segments.length; ++j)ensureSegment(j).lock(); // force creation
            }sum = 0L;size = 0;overflow = false;for (int j = 0; j < segments.length; ++j) {Segment<K,V> seg = segmentAt(segments, j);if (seg != null) {sum += seg.modCount;int c = seg.count;if (c < 0 || (size += c) < 0)overflow = true;}}if (sum == last)break;last = sum;}} finally {if (retries > RETRIES_BEFORE_LOCK) {for (int j = 0; j < segments.length; ++j)segmentAt(segments, j).unlock();}}return overflow ? Integer.MAX_VALUE : size;
}


为什么这样设计呢?这种思想和乐观锁悲观锁的思想如出一辙。


为了尽量不锁住所有Segment,首先乐观地假设Size过程中不会有修改。当尝试一定次数,才无奈转为悲观锁,锁住所有Segment保证强一致性。





几点说明:


1. 这里介绍的ConcurrentHashMap原理和代码,都是基于Java1.7的。在Java8中会有些许差别。


2.ConcurrentHashMap在对Key求Hash值的时候,为了实现Segment均匀分布,进行了两次Hash。有兴趣的朋友可以研究一下源代码。




—————END—————




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

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

相关文章

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…

漫画:什么是Base64算法

转载自 玻璃猫 程序员小灰 ValueChar ValueChar ValueChar ValueChar0A16Q32g48w1B17R33h49x2C18S34i50y3D19T35j51z4E20U36k5205F21V37l5316G22W38m5427H23X39n5538I24Y40o5649J25Z41p57510K26a42q58611L27b43r59712M28c44s60813N29d45t61914O30e46u6215P31f47v63/控制字符&am…

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

soapui 测试soap在这个分为三部分的系列中&#xff0c;我将演示如何使用SoapUI API工具来调用安全的WCF SOAP服务。 第一篇文章将着重于创建将要测试的系统的服务。 第二篇文章将介绍在基本身份验证机制保护的情况下调用它所需的步骤。 在最后一部分中&#xff0c;我将对初始服…

dmn是大脑中哪个区域_DMN中的函数式编程:感觉就像再次重读我的大学课程一样...

dmn是大脑中哪个区域在本文中&#xff0c;我想分享有关DMN中递归支持的有趣见解&#xff0c;并重点介绍FEEL语言的特定属性如何使功能性编程结构能够在DMN中建模。 我们将从一个基本示例开始&#xff0c;以演示FEEL语言和DMN构造的“商业友好”性质如何使我们能够解决一个通常…