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版本中,我们可以期待更多的优化和改进,以满足不断增长的并发编程需求。

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

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

相关文章

广汽埃安工厂:蔚来汽车的造车工厂有哪些?

具体来说,理想汽车目前在常州仅有一家汽车制造工厂。 一期项目于2017年12月竣工,2019年12月投产,年产能10万辆/年。 同时,正在规划二期工程。 产能将增至20万辆/年。 此外,理想还计划接管现代汽车在北京顺义的第一家工…

工业现场网络性能评估方案

最近要去一个工厂排查网络和电脑卡顿的问题,为此,我准备了以下的方案,在现场以抓包和网络监控的方式来排查。 1.评估流程 为了评估Linux系统的网络负荷,并使用tcpdump来捕获数据包进行分析,您需要遵循以下几个步骤: …

数据仓库与数据挖掘概述

目录 一、数据仓库概述 (一)从传统数据库到数据仓库 (二)数据仓库的4个特征 (三)数据仓库系统 (四)数据仓库系统体系结构 (五)数据仓库数据的粒度与组织…

论文阅读_代码生成模型_CodeGeeX

英文名称: CodeGeeX: A Pre-Trained Model for Code Generation with Multilingual Evaluations on HumanEval-X 中文名称: CodeGeeX:一种用于代码生成的预训练模型,并在HumanEval-X上进行多语言评估 链接: https://arxiv.org/abs/2303.17568 代码: http…

训练1 : 老头

以前用blender做的特效 总结 头发很费时间, 需要参考和练习眼窝周边结构还有些待准确把握从光与影中揣摩轮廓形状 从少量面掌握大体, 从多数面雕刻细节

terminal下环境不统一导致的程序报错(powersell改cmd)

1.报错现象 在terminal下利用命令行执行代码显示运行环境缺包: 但将命令中的参数写入参数文件,运行train.py时,程序可以正常运行: 直接运行train.py:程序可用: 2.原因分析 参考文章 控制台环境和项目环境不一致问…

【Mysql】InnoDB 中 B+ 树索引的注意事项

一、根页面万年不动 在之前的文章里,为了方便理解,都是先画存储用户记录的叶子节点,然后再画出存储目录项记录的内节点。 但实际上 B 树的行成过程是这样的: 每当为某个表创建一个 B 树索引,都会为这个索引创建一个根…

循环队列的实现

文章目录 循环队列的概念循环队列的实现循环队列的判空和判满链表or数组 循环队列的概念 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓…

快速下载Huggingface的大语言模型

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Huggingface是什么?二、基于官方huggingface-cli下载(基础,断线风险)1.安装hf下载环境2.配置环境变量3.注册…

CSAPP-信息的表示和处理

文章目录 概念扫盲思想理解经典好图安全事件 概念扫盲 1.大端高位在前,小端低位在前 2.逻辑运算符(&& 、||、!)与位级运算(&、|、~)的差异 3.宏可以保证无论代码如何编译,都能生成…

flutterandroidx支持,【工作经验分享】

基于Linux的pc启动过程 我们都知道,所有的程序软件包括操作系统都是运行在内存中的,然而我们的操作系统一般是存放在硬盘上的,当我们按下开机键的时候,此时内存中什么程序也没有,因此需要借助某种方式,将操…

百度SEO工具,自动更新网站的工具

在网站SEO的过程中,不断更新网站内容是提升排名和吸引流量的关键之一。而对于大多数网站管理员来说,频繁手动更新文章并进行SEO优化可能会是一项繁琐且耗时的任务。针对这一问题,百度自动更新文章SEO工具应运而生,它能够帮助网站管…

chartjs 饼状图

之前要把canvas先清除掉&#xff0c;不然刷新数据&#xff0c;还会有前面的图表 function clearCanvas(){$(#donutChart).remove();$(#chartdiv).append(<canvas id"donutChart" style"min-height: 500px; height: 500px; max-height: 500px; max-width: 70%…

浅谈mysql mvcc

目录 前言 mvcc 是如何工作的&#xff1f; 数据的更新 前言 mvcc 与一个事物的隔离级别有关&#xff0c;未提交读永远读的是当前值&#xff0c;串行化是通过加锁实现&#xff0c;这两种隔离级别都与mvcc 没有任何关系。只要一提到mvcc应该想到的是读提交以及可重复读&#…

vue+element ui上传图片到七牛云服务器

本来打算做一个全部都是前端完成的资源上传到七牛云的demo&#xff0c;但是需要获取token&#xff0c;经历了九九八十一难&#xff0c;最终还是选择放弃&#xff0c;token从后端获取&#xff08;springboot&#xff09;。如果你们有前端直接能解决的麻烦记得私我哦&#xff01;…

『Linux从入门到精通』第 ㉒ 期 - 动静态库

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;什么是库&#xff1f;&#x1f427;为什么要有库&#xff1f;&#x1f427;写一个自己的库&#x1f426;方法一&#x1f426;方法二 静态库&#x1f426;标准化&#x1f426;方法三 动态库&#x1f426;配置动…

python中自定义报错

class MyError(Exception):def __init__(self,num):#录入的数Exception.__init__(self)self.numnumdef __str__(self):return 这是我定义的第%d个异常 %(self.num)使用 try:raise MyError(4) except MyError as e:print(e)raise 其作用是指定抛出的异常名称&#xff0c;以及异常…

数据结构题目①——数组

前言 本篇文章为博主进行代码随想录——数组练习后的总结会涉及到每一道题目的详细的思路整理&#xff0c;以及本人的易错点&#xff0c;希望对大家有所帮助 数组介绍&#xff1a; 数组在C语言中就已经有所涉及&#xff0c;它是一个最基础的数据结构&#xff0c;而在数据结构中…

three 层级模型

group.remove(mesh1,mesh2);Vector3与模型位置、缩放属性 Group层级模型(树结构) 创建了两个网格模型mesh1、mesh2&#xff0c;通过THREE.Group类创建一个组对象group,然后通过add方法把网格模型mesh1、mesh2作为设置为组对象group的子对象&#xff0c;然后在通过执行scene.a…

jenkins部署maven项目

流程&#xff1a; jenkins从代码仓库读取代码&#xff0c;将代码文件放入jenkins的工作空间&#xff0c;将jenkins工作空间的代码进行打包&#xff0c;将jar包远程发送给服务器。 一&#xff1a;所需插件二&#xff1a;Tools 三&#xff1a;System&#xff1a; 配置ssh连接的…