ConcurrentHashMap的演进:从Java 8之前到Java 17的实现原理深度剖析

目录

    • 一、引言
    • 二、Java 8之前的ConcurrentHashMap
      • 1、内部结构与初始化
      • 2、Segment类
      • 3、并发控制
      • 4、扩容与重哈希
      • 5、总结
    • 三、Java 8中的ConcurrentHashMap
      • 1、数据结构
      • 2、并发控制
        • 2.1. CAS操作
        • 2.2. synchronized同步块
      • 3、哈希计算与定位
      • 4、扩容与重哈希
      • 5、总结
    • 四、Java 17中的ConcurrentHashMap
      • 1、数据结构
      • 2、并发控制
      • 3、哈希计算与定位
      • 4、扩容与重哈希
      • 5、其他改进和优化
    • 五、总结

一、引言

在Java的并发编程中,ConcurrentHashMap以其出色的并发性能和数据一致性成为了众多开发者的首选。从Java 5的引入至今,ConcurrentHashMap经历了多次重大的改进和优化。本文将详细深入全面地探讨从Java 8之前到Java 17中ConcurrentHashMap的实现原理及其变化。

二、Java 8之前的ConcurrentHashMap

在Java 8之前,ConcurrentHashMap的实现原理主要基于分段锁(Segmentation Lock)的机制,这种设计使得它能够在高并发环境下提供良好的性能。以下是详细的介绍:

在这里插入图片描述

1、内部结构与初始化

ConcurrentHashMap内部主要由三个组件构成:一个Segment数组、哈希函数和键值对节点。其中,Segment是一个可重入的互斥锁,每个Segment包含一个哈希表,哈希表中的每个元素都是一个链表。

在初始化ConcurrentHashMap时,会创建一个Segment数组,并指定初始容量和负载因子。每个Segment的初始容量和负载因子与整个ConcurrentHashMap的相同。此外,还会为每个Segment分配一个锁,用于控制对该Segment的并发访问。

2、Segment类

Segment类是ConcurrentHashMap实现并发控制的核心。它继承自ReentrantLock,拥有自己的锁,并且包含一个哈希表。Segment类中的哈希表结构与普通的HashMap类似,采用链表解决哈希冲突。每个链表节点包含一个键值对和一个指向下一个节点的引用。

除了哈希表之外,Segment还维护了一些统计信息,如元素数量、修改次数等。这些信息用于支持扩容和迭代器操作。

3、并发控制

当线程需要访问ConcurrentHashMap中的某个键时,它会首先计算键的哈希值,并根据哈希值的高位定位到对应的Segment。然后,线程会尝试获取该Segment的锁。如果锁已经被其他线程持有,则当前线程会等待直到获取锁为止。

一旦线程获得Segment的锁,它就可以在该Segment内部进行哈希表的查找、插入或删除操作。这些操作与普通的HashMap类似,但需要在锁的保护下进行以确保线程安全。完成操作后,线程会释放锁,使得其他线程有机会访问该Segment

需要注意的是,虽然每个Segment都有自己的锁,但整个ConcurrentHashMap的并发性能并不完全取决于锁的数量。实际上,锁的竞争程度、哈希函数的分布性以及负载因子等因素都会对并发性能产生影响。

4、扩容与重哈希

当某个Segment的负载因子超过阈值时,会触发扩容操作。扩容时,会创建一个新的Segment数组,并将原有Segment中的键值对重新散列到新的Segment数组中。这个过程涉及到大量的数据复制和重哈希计算。

为了减少扩容对并发性能的影响,ConcurrentHashMap采用了分段扩容的策略。它每次只处理一个Segment,并且在扩容过程中仍然允许其他线程访问未处理的Segment。这样确保了扩容操作不会阻塞整个ConcurrentHashMap的并发访问。

此外,在扩容过程中,ConcurrentHashMap还采用了一种称为“转移策略”的技术来避免死锁和饥饿问题。具体来说,当某个线程正在处理一个Segment时,如果该Segment需要扩容,那么扩容操作会由另一个线程来完成。这样确保了处理线程不会因等待扩容而阻塞过长时间。

5、总结

Java 8之前的ConcurrentHashMap通过分段锁的设计实现了高并发性能。它将哈希表划分为多个段,并使用细粒度的锁来控制对每个段的访问。这种设计大大减少了锁的竞争,提高了并发性能。然而,随着Java版本的迭代和硬件性能的提升,分段锁的设计逐渐暴露出一些问题,如内存占用较大、扩容操作复杂等。

三、Java 8中的ConcurrentHashMap

在Java 8中,ConcurrentHashMap的实现原理发生了显著的变化,它摒弃了之前版本中的分段锁(Segmentation Lock)机制,转而采用了一种更为高效和灵活的并发控制策略,即CAS(Compare-and-Swap)操作结合synchronized同步块。这种新的设计不仅简化了数据结构,还提高了在多核处理器环境下的并发性能。

1、数据结构

Java 8中的ConcurrentHashMap底层数据结构主要由数组、链表和红黑树组成。数组用于存储键值对的节点,每个节点要么是一个链表,要么是一个红黑树。当链表长度超过一定阈值(默认为8)时,链表会转换为红黑树,以提高搜索效率。
在这里插入图片描述

2、并发控制

2.1. CAS操作

CAS(Compare-and-Swap)是一种无锁化的算法,它包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置的值更新为新值B。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。在ConcurrentHashMap中,CAS操作被广泛应用于节点的添加、删除和更新等场景,以确保并发修改的安全性。

2.2. synchronized同步块

尽管CAS操作能够在很大程度上减少锁的竞争,但在某些情况下,仍然需要更严格的同步机制来保证并发操作的正确性。因此,Java 8中的ConcurrentHashMap在必要时会使用synchronized同步块来保护某些关键代码段,如树化操作、扩容等。与分段锁相比,synchronized同步块具有更低的开销和更高的灵活性。

3、哈希计算与定位

与之前的版本类似,Java 8中的ConcurrentHashMap也使用哈希算法来计算键的哈希值,并根据哈希值来定位数组中的索引位置。不同的是,Java 8中的哈希计算过程更加复杂和精细,以减少哈希冲突和提高空间利用率。此外,当发生哈希冲突时,新的键值对会添加到链表或红黑树的末尾,而不是像之前版本那样使用头插法。

4、扩容与重哈希

ConcurrentHashMap中的元素数量超过数组的容量阈值时,就会触发扩容操作。在扩容过程中,会创建一个新的数组,并将原有数组中的键值对重新散列到新的数组中。与之前的版本不同,Java 8中的扩容操作不再需要对整个数组进行锁定,而是采用了更细粒度的并发控制策略。具体来说,它将数组划分为多个小段(每个小段包含多个桶),并允许多个线程同时处理不同的小段。这样设计可以减少锁的竞争和提高扩容操作的并发性能。

5、总结

Java 8中的ConcurrentHashMap通过采用CAS操作结合synchronized同步块的并发控制策略以及优化后的数据结构和哈希算法等技术手段实现了高并发性能下的线程安全访问。与之前的版本相比,它在简化数据结构、提高空间利用率和降低锁竞争等方面取得了显著的进步。这些改进使得ConcurrentHashMap成为Java并发编程中不可或缺的重要组件之一。

四、Java 17中的ConcurrentHashMap

在Java 17中,ConcurrentHashMap的实现原理基本保持了Java 8引入的设计,但可能包含了一些优化和改进,以适应新的JDK版本和硬件环境。以下是Java 17中ConcurrentHashMap实现原理的深入介绍:

1、数据结构

与Java 8相似,Java 17中的ConcurrentHashMap也使用了数组、链表和红黑树作为底层数据结构。数组用于存储键值对的节点,每个节点在哈希冲突时形成链表,当链表长度超过一定阈值(默认为8)并且数组长度大于64时,链表会转换为红黑树,以提高搜索效率。如果数组长度小于等于64,则不会进行树化,而是采用扩容来减少哈希冲突。

2、并发控制

Java 17中的ConcurrentHashMap仍然采用CAS操作和synchronized同步块来实现并发控制。CAS操作用于无锁化的节点添加、删除和更新等操作,而synchronized同步块则用于保护树化、扩容等需要更严格同步的代码段。

不过,在Java 17中,JDK可能对这些操作进行了进一步的优化,以减少不必要的CAS操作和锁竞争,提高并发性能。例如,通过更精细的粒度控制synchronized同步块的范围,或者使用更高效的锁实现等。

3、哈希计算与定位

Java 17中的ConcurrentHashMap哈希计算过程与Java 8类似,但可能包含了一些针对新硬件环境的优化。哈希值用于定位数组中的索引位置,当发生哈希冲突时,新的键值对会添加到链表或红黑树的末尾。

此外,Java 17中的ConcurrentHashMap可能还引入了一些新的哈希算法或哈希冲突解决策略,以进一步减少哈希冲突和提高空间利用率。

4、扩容与重哈希

ConcurrentHashMap中的元素数量超过数组的容量阈值时,会触发扩容操作。在Java 17中,扩容操作的基本原理与Java 8相似,即创建一个新的数组,并将原有数组中的键值对重新散列到新的数组中。然而,Java 17可能对扩容过程中的并发控制、数据迁移等方面进行了优化和改进。

例如,通过更细粒度的并发控制策略来减少锁的竞争;使用更高效的数据迁移算法来减少扩容过程中的性能开销;或者引入一些新的技术手段来提高扩容操作的并发性能和可靠性等。

5、其他改进和优化

除了上述基本原理外,Java 17中的ConcurrentHashMap还包含一些其他改进和优化:

  • 更好的内存布局和缓存利用:通过优化数据结构的内存布局和访问模式,提高缓存利用率和减少内存访问开销。
  • 更高效的节点操作:通过优化节点的添加、删除和更新等操作,减少不必要的内存分配和垃圾回收开销。
  • 更灵活的参数配置:提供更多的参数配置选项,以便用户根据具体应用场景进行更精细的性能调优。
  • 更完善的错误处理和异常处理机制:增强错误处理和异常处理能力,提高程序的健壮性和可靠性。

总之,在Java 17中,ConcurrentHashMap仍然是一个高性能、线程安全的并发哈希表实现,它在数据结构、并发控制、哈希计算与定位以及扩容与重哈希等方面都进行了深入的设计和优化。

五、总结

从Java 8之前到Java 17,ConcurrentHashMap经历了显著的演进。Java 8之前的版本采用分段锁机制实现并发控制;Java 8引入了红黑树和更细粒度的锁策略来优化性能;而Java 17在保持Java 8基本设计的同时,对并发控制和内部实现进行了进一步的优化和改进。这些变化使得ConcurrentHashMap在并发性能、内存开销和稳定性等方面不断得到提升和完善。作为Java并发编程中的重要组成部分,ConcurrentHashMap的演进历程反映了Java平台对并发性能和稳定性的持续追求和提升。在未来的Java版本中,我们可以期待更多的优化和改进,以满足不断增长的并发编程需求。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker知识点总结

二、Docker基本命令: Docker支持CentOs 6 及以后的版本; CentOs7系统可以直接通过yum进行安装,安装前可以 1、查看一下系统是否已经安装了Docker: yum list installed | grep docker 2、安装docker: yum install docker -y -y 表示自动确认…

安装Realtek Audio Driver失败[Error Code:-0001]

安装Realtek Audio Driver失败[Error Code:-0001] 首先来看一下我们遇到的问题GPT4的推荐解决方法(流水账)笔者自己真实有效的解决办法 首先来看一下我们遇到的问题 描述:在笔记本更新完电脑之后,没有自带声音驱动。然…

【LeetCode】升级打怪之路 Day 11:栈的应用、单调栈

今日题目: Problem 1: 栈的应用 155. 最小栈 | LeetCode20. 有效的括号 | LeetCode150. 逆波兰表达式求值 | LeetCode Problem 2: 单调栈 496. 下一个更大元素 I739. 每日温度503. 下一个更大元素 II 目录 Problem 1:栈 - “先进后出”的应用LC 155. 最…

2024龙年特别篇 -- 魔法指针 之 指针变量

目录 ​编辑 字符指针变量 字符指针 字符数组 关于字符数组的试题 数组指针变量 数组指针 利用指针数组打印数组 打印二维数组 数组作为形参 当形参为指针时(指针数组) 函数指针变量 利用函数实现加法输出的多种方式 字符指针变量 字符指针 char …

[NSSCTF 2nd] web复现

1.php签到 <?phpfunction waf($filename){$black_list array("ph", "htaccess", "ini");$ext pathinfo($filename, PATHINFO_EXTENSION);foreach ($black_list as $value) {if (stristr($ext, $value)){return false;}}return true; }if(i…

CKA考生注意:这些Deployment要点能助你一臂之力!

往期精彩文章 : 提升CKA考试胜算&#xff1a;一文带你全面了解RBAC权限控制&#xff01;揭秘高效运维&#xff1a;如何用kubectl top命令实时监控K8s资源使用情况&#xff1f;CKA认证必备&#xff1a;掌握k8s网络策略的关键要点提高CKA认证成功率&#xff0c;CKA真题中的节点维…

蓝桥杯集训·每日一题2024 (前缀和)

笔记&#xff1a; 例题&#xff1a; #include<bits/stdc.h> using namespace std; const int N 5000010; char str[N]; int s[N]; int main(){int t;cin>>t;for(int a1;a<t;a){int n;cin>>n;scanf("%s",str1);for(int i1;i<n;i){s[i]s[i-1]…

重磅!交通领域顶级会议TRB会议将进行重大改革

美国交通研究委员会年会&#xff08;Transportation Research Board annual meeting,以下简称TRB会议&#xff09;是由美国交通研究委员会举办的交通领域的国际顶级会议。该会议每年举办一次&#xff0c;在华盛顿特区召开。TRB会议是交通研究领域知名度最高的学术会议之一&…

AI又进化了

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 一直想做但没做的板块&#xff0c;整理一段时间内AI领域的前沿动态&#xff08;符合大多粉丝研究领域/感兴趣方向&#xff09;&#xff0c;了解了解外面世界发展成啥样了&#xff0c;一起看看吧~ 谷歌…

跟 AI 学 StarRocks:简介

因为要支持公司的 BI 建设&#xff0c;团队引入了 StarRocks 数据库&#xff0c;此前我没有了解过此项技术&#xff0c;不过因为有架构师引入了此项技术栈&#xff0c;就顺便学习一下。 一、什么是 MPP 数据库&#xff1f; MPP 数据库指的是大规模并行处理&#xff08;Massiv…

Hololens 2应用开发系列(2)——MRTK基础知识及配置文件配置(上)

Hololens 2应用开发系列&#xff08;2&#xff09;——MRTK基础知识及配置文件配置 一、前言二、MRTK基础知识2.1 MRTK概述2.2 MRTK运行逻辑2.3 MRTK配置文件介绍2.4 MRTK服务 三、配置文件使用3.1 总配置文件3.2 相机配置3.3 其他配置 参考文献 一、前言 在前面的文章中&…

DDR5 新特性概述

主页&#xff1a; 元存储博客 文章目录 前言1. SDR 与 DDR2. DDR5 的新特点总结 前言 DDR5 带来更快的处理速度和更大的存储空间&#xff0c;为云计算、大数据等领域的发展提供了强有力的支持。 1. SDR 与 DDR single data rate&#xff0c; 1 个时钟周期做一次数据传输 do…

更改elementui的箭头图片以及位置

//更改箭头位置 .el-tree-node__content > .el-tree-node__expand-icon {position: absolute;right: 12px; }//更改箭头图片 .el-tree-node__expand-icon {transform: rotate(-90deg); } .el-tree-node__expand-icon.expanded {transform: rotate(0deg); } // 有子节点 且已…

使用QEMU搭建U-Boot+LinuxKernel+busybox+NFS嵌入式开发环境

目录 0.课程大纲1.为什么要使用QEMU学习嵌入式QEMU简介使用QEMU可以做哪些事情?当前嵌入式行业现状如何适应这种变化使用QEMU学习嵌入式有哪些好处?驱动开发技能为什么要学习Linux 2.搭建嵌入式开发基本环境2.1.安装u-boot-tools2.2.安装交叉编译工具什么是ABI和EABI 3.QEMU安…

DSI2协议之BTA行为理解

概念: DSI协议spec支持总线控制权在master和slave之间发生交换,即通过bus turn around来实现; BUS TURN AROUND: BTA 的实现是通过controller—>cdphy的turnrequest信号来实现; 关于控制器发出turnrequest给phy,phy通过lvds/trio线输出turnaround sequence如下图中…

C++基于多设计模式下的同步异步日志系统day3

C基于多设计模式下的同步&异步日志系统day3 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&am…

C习题002:澡堂洗澡

问题 输入样例 在这里给出一组输入。例如&#xff1a; 2 5 1 3 3 2 3 3 输出样例 在这里给出相应的输出。例如&#xff1a; No代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB 代码 #include<stdio.h> int main() {int N,W,s,t,p;int arr_s[…

投影和定义投影的区别

Arcmap中关于投影的工具有四个&#xff0c;分别是定义投影、投影、投影栅格、批量投影。这四个工具既有相同之处也有不同之处&#xff0c;下面我将一一介绍。 ①定义投影&#xff1a;Arcmap中关于定义投影工具是这样描述的&#xff1a;“所有地理数据集均具有一个用于显示、测…

小白必看的Python函数讲解

定义函数 我们通过斐波那契数列来理解定义函数 >>> def fib(n): # 将斐波那契数列打印到 n ... """将斐波那契数列打印到 n""" ... a, b 0, 1 ... while a < n: ... print(a, end ) ... a, b b, …

【Linux安装软件命令及vim、gcc使用说明】

安装软件命令 Linux安装软件的命令首先要进入管理员权限 首先在终端输入sudo su切换到管理员界面 输入对应的密码&#xff0c;注意这里的密码不会显示出来&#xff0c;输完密码之后回车即可。当出现root就代表已经是管理员界面了。 如果相应退出管理员界面输入exit即可。 注…