ConcurrentHashMap-Java八股面试(五)

news2025/1/11 9:31:05

系列文章目录

第一章 ArrayList-Java八股面试(一)

第二章 HashMap-Java八股面试(二)

第三章 单例模式-Java八股面试(三)

第四章 线程池和Volatile关键字-Java八股面试(四)


提示:动态每日更新算法题,想要学习的可以关注一下

文章目录

  • 系列文章目录
  • 一、ConcurrentHashMap是什么?
    • 1 jdk1.7
      • 1. put方法
      • 2. HashEntry扩容
      • 3 Segment的数量和下标的计算
      • 4.HashEntry的初始化
      • 5.HashEntry的下标计算
    • 2. jdk1.8
      • 1. 扩容机制
      • 2. 初始化机制(capacity和factor)
      • 3. put方法
      • 4. 扩容机制
      • 5. 扩容时的put和get操作
  • 总结和细节
    • 1.JDK1.7 与 JDK1.8 中ConcurrentHashMap 的区别
    • 2.ConcurrentHashMap 的并发度是什么?
    • 3. ConcurrentHashMap 迭代器是强一致性还是弱一致性
    • 4.ConcurrentHashMap 和 Hashtable 的效率哪个更高?为什么?


一、ConcurrentHashMap是什么?

1 jdk1.7

JDK1.7 中的 ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成,即 ConcurrentHashMap 把哈希桶数组切分成小数组(Segment ),每个小数组有 n 个 HashEntry 组成。
和普通的HashMap相比,ConcurrentHashMap其实就解决了多线程下安全的问题,其构造和HashMap相差不大,也是需要计算二次哈希值,唯一需要说明的是segment下标和小数组的问题。
在这里插入图片描述

Segment的数量决定了ConcurrentHashMap的并发度,因为在同一时刻只有一把锁可以访问,一旦Segment初始化之后就无法扩容,能扩容的是Segment下面的小数组。
而每个小数组则是HashEntry。

1. put方法

每一个元素先进行二次哈希值的计算,然后计算得到Segment下标,而后再进算桶下标,最后会以头插法的方式加入到链表中。
在这里插入图片描述

2. HashEntry扩容

一旦HashEntry下的节点个数大于HashEntry数量的3/4时就会发生扩容
在这里插入图片描述

在这里插入图片描述

3 Segment的数量和下标的计算

segment的数量是初始化时给的参数(clevel).
而segment的下标计算时根据如下图所示是二次哈希值的二进制编码的高4位得出12。

在这里插入图片描述

4.HashEntry的初始化

一旦初始化ConcurrentHashMap后,第一个HashEntry也会随之初始化,HashEntry的数量计算时:capacity/Segment(clevel),当然最小为2.第一次创建之后这个也就会作为模板,后面任意一个Segment对HashEntry初始化时都会以这个长度,而不是第一个HashEntry扩容后的长度(此时的长度)。注意这里的capacity只在这一步计算中用到,我们看到的数组长度其实是segment的长度。

在这里插入图片描述

5.HashEntry的下标计算

二次哈希值的二进制编码的最低位,如下图为0,则在索引为0的地方。
在这里插入图片描述

2. jdk1.8

与jdk1.7不同的是没有了Segment,也没有了HashEntry,和原始的HashMap在结构上有一些类似。初始化方式为懒汉式,只有在调用时才会初始化整个ConcurrentHashMap.在数据结构上, JDK1.8 中的ConcurrentHashMap 选择了与 HashMap 相同的Node数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用CAS + synchronized实现更加细粒度的锁。

将锁的级别控制在了更细粒度的哈希桶数组元素级别,也就是说只需要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。

在这里插入图片描述

1. 扩容机制

与1.7不同的是,当容量到达3/4时就会发生扩容,而不是超过3/4。
在这里插入图片描述

2. 初始化机制(capacity和factor)

在这里插入图片描述
capacity的意思并不再指初始化数组的容量,而是指一开始会往ConcurrentHashMap中放几个元素。
如上图所示,放16个元素,扩容因子为3/4,假如map长度为16,那么长度在12就会扩容,因此还需要在16的基础上继续扩容成32.尽管capacity设置为16,但并不意味着必须要放16个元素,并且capacity和factor只会在第一次初始化时有效。后续就算factor设置为0.5或者其他值,都是按默认的0.75计算。

在这里插入图片描述

值得说明的是,ConcurrentHashMap的容量只能是2的n次方。如上所示放7个元素,那么先考虑8是否能行,因为factor为0.5,所以必须要扩容成16.

3. put方法

在这里插入图片描述

根据二次哈希值先定位到数组索引处,然后占锁,再进行put。数组的大小决定了
ConcurrentHashMap的并发度,也就是说不同索引的Node互不干扰,d的put和ab的put互不影响。但是a和b的put需要占锁释放锁的步骤。

4. 扩容机制

在这里插入图片描述

假设n为临界节点,也就是put了n之后就会扩容,与数组方式相同,新建一个容量为2倍的新数组,然后进行数据迁移。
在这里插入图片描述

迁移顺序是从后往前进行迁移,每次迁移一个索引的链表或者红黑树。一旦该索引处完成迁移,就会变成Forwarding Node 认为是已完成的Node。

在这里插入图片描述

在迁移过程中,需要重新计算二次哈希值,一般情况下会重新创建新的节点,并将该节点插入新的数组中。
在这里为什么需要重新创建新的节点需要说明一下,如上图1这个节点的next指针为2,一旦哈希值重新计算之后,在新的数组中,它的next不一定是2.
当然也有一种情况可以直接引用,不需要再创建节点的情况就是,比如说456三个节点,在新的数组中仍然是该顺序情况,那么就不需要新创建节点。

5. 扩容时的put和get操作

  1. get的索引在forwarding node之前
    那么正常get,并不会影响。

2.get的索引已经变成forwarding
那么直接去新数组中取得get

3.get的索引正在迁移
由上所述,因为扩容之后的节点的next指针可能会变化,所以该线程会等待该索引完成迁移查询新的节点。

在这里插入图片描述

  1. put的索引在forwarding node之前
    同样,正常的并发执行。
    2.put的索引已经变成forwarding node
    线程会阻塞并帮助前面的node一起进行迁移,因为新的节点只有在所有旧的节点都完成迁移之后才能进行put,这时线程就会帮助前面未完成的索引一起进行迁移。
    3.put的索引正在迁移
    只能阻塞。

总结和细节

1.JDK1.7 与 JDK1.8 中ConcurrentHashMap 的区别

数据结构:取消了 Segment 分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
保证线程安全机制:JDK1.7 采用 Segment 的分段锁机制实现线程安全,其中 Segment 继承自 ReentrantLock 。JDK1.8 采用CAS+synchronized保证线程安全。
锁的粒度:JDK1.7 是对需要进行数据操作的 Segment 加锁,JDK1.8 调整为对每个数组元素加锁(Node)。
链表转化为红黑树:定位节点的 hash 算法简化会带来弊端,hash 冲突加剧,因此在链表节点数量大于 8(且数据总量大于等于 64)时,会将链表转化为红黑树进行存储。
查询时间复杂度:从 JDK1.7的遍历链表O(n), JDK1.8 变成遍历红黑树O(logN)。

2.ConcurrentHashMap 的并发度是什么?

并发度可以理解为程序运行时能够同时更新 ConccurentHashMap且不产生锁竞争的最大线程数。在JDK1.7中,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度,默认是16,这个值可以在构造函数中设置。

在JDK1.8中,已经摒弃了Segment的概念,选择了Node数组+链表+红黑树结构,并发度大小依赖于数组的大小。

3. ConcurrentHashMap 迭代器是强一致性还是弱一致性

与 HashMap 迭代器是强一致性不同,ConcurrentHashMap 迭代器是弱一致性。

ConcurrentHashMap 的迭代器创建后,就会按照哈希表结构遍历每个元素,但在遍历过程中,内部元素可能会发生变化,如果变化发生在已遍历过的部分,迭代器就不会反映出来,而如果变化发生在未遍历过的部分,迭代器就会发现并反映出来,这就是弱一致性。

4.ConcurrentHashMap 和 Hashtable 的效率哪个更高?为什么?

ConcurrentHashMap 的效率要高于 Hashtable,因为 Hashtable 给整个哈希表加了一把大锁从而实现线程安全。而ConcurrentHashMap 的锁粒度更低,在 JDK1.7 中采用分段锁实现线程安全,在 JDK1.8 中采用CAS+synchronized实现线程安全。

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

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

相关文章

基于卷积神经网络CNN的三相故障识别

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络CNN手写体识别 基本结构 主要参数 MATALB代码 结果图 展望 背影 现在生活,为节能减排,减少电能损…

Ajax学习 基础概念 发送请求 常见方法

AJAX 简介 AJAX 全称为Asynchronous Javascript And XML ,就是异步的JS和XML。 通过AJAX可以再浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。 AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。…

从FPGA说起的深度学习(四)

这是新的系列教程,在本教程中,我们将介绍使用 FPGA 实现深度学习的技术,深度学习是近年来人工智能领域的热门话题。在本教程中,旨在加深对深度学习和 FPGA 的理解。用 C/C 编写深度学习推理代码高级综合 (HLS) 将 C/C 代码转换为硬…

锚点定位使内容在指定区域显示

1、问题描述 在使用锚点定位时,使用 scrollIntoView 方法,导致整个页面随着锚点跳转 2、问题分析 因为最开始做需求的时候,只在最外层设置了一个滚动条,所以导致整体锚点的跳转是随着最外层滚动条距离顶部的高度跳转的&#xf…

从NLP视角看电视剧《狂飙》,会有什么发现?

文章目录1、背景2、数据获取3、文本分析与可视化3.1 短评数据预处理3.2 词云图可视化3.3 top关键词共现矩阵网络3.4 《狂飙》演职员图谱构建4、短评相关数据分析与可视化5、总结原文请关注:实用自然语言处理 作者:风兮 建议查看原文: https…

Linux下软件部署安装管理----rpmbuild打包rpm包部署安装

来源:微信公众号「编程学习基地」 文章目录1.安装rpmbuild2.rpm包制作打包rpm包3.rpm包安装4.rpm包卸载1.安装rpmbuild yum install rpmbuild yum install rpmdevtools创建rpm包管理路径,生成rpm相关目录 RPM打包的时候需要编译源码,还需要…

基于Cortex-M7内核STM32F767NIH6,STM32F767VGT6,STM32F767VIT6嵌入式技术资料

STM32F7 32 位 MCUFPU 基于高性能的 ARMCortex-M7 32 位 RISC 内核,工作频率高达 216MHz。Cortex-M7 内核具有单浮点单元(SFPU)精度,支持所有 ARM 单精度数据处理指令与数据类型。同时执行全套 DSP 指令和存储保护单元(MPU)&#…

【完美解决】应用程序无法正常启动(0xc000007b)请单击“确定”关闭应用程序

年期安装CorelDRAW X8 (64-Bit),安装完成之后运行一点毛病都没有,可是过了两三个月,再打开就出现“应用程序无法正常启动(0xc000007b)请单击“确定”关闭应用程序”这个提示框,如下图示 出现这个问题我就上网查找,无非…

Java学习笔记 --- JavaScript

一、JavaScript介绍 JavaScript语言诞生主要是完成页面的数据验证。因此它运行在客户端,需要运行浏览器来解析执行JavaScript代码。JS是Netcape网景公司的产品,最早取名为LiveScript;为了吸引更多java程序员。更名为 JavaScript JS是弱类型&…

File 文件操作

File 文件操作: 一、常用方法: 方法类型描述public File(String pathname)构造给定一个要操作文件的完整路径public File(File parent, String child)构造给定要操作文件的父路径和子文件名称public boolean createNewFile() throws IOExce…

hexo部署github搭建个人博客 完整详细带图版(更新中)

文章目录0. 前置内容1. hexo创建个人博客2. GitHub创建仓库3. hexo部署到GitHub4. 常用命令newcleangenerateserverdeploy5. 添加插件5.1 主题5.2 博客基本信息5.3 创建新的菜单5.4 添加搜索功能5.5 添加阅读时间字数提示5.6 打赏功能5.7 切换主题5.8 添加不蒜子统计5.9 添加百…

小程序的拉流组件live-player的使用

前言: 我们在小程序中实现音视频-直播/录播 的播放时候,会使用到微信官方提供的两个组件,推流组件和拉流组件,这里来分享下他的拉流组件的使用和具体需要注意的点。 效果图: 1、拉流状态code日志 2、代码使用截图&am…

深度学习 Day26——利用Pytorch实现天气识别

深度学习 Day26——利用Pytorch实现天气识别 文章目录深度学习 Day26——利用Pytorch实现天气识别一、前言二、我的环境三、前期工作1、导入依赖项和设置GPU2、导入数据3、划分数据集四、构建CNN网络五、训练模型1、设置超参数2、编写训练函数3、编写测试函数4、正式训练六、结…

前端利用emailjs发送邮件

最近有一个需求,前端发送一个form表单到一个邮箱,找了一圈发现emailjs还不错就使用他了。首先emailjs官网注册一个账号注册完之后创建一个邮件服务(我这里使用的是谷歌邮箱)链接谷歌邮箱账户 然后创建服务接下来就要创建一个邮件的…

浅谈入门Servlet注解式开发

Servlet3.0版本之后,推出了Servlet基于注解式开发。 优点:开发效率高,直接在java类上使用注解进行标注,可直接省略WEB.xml文件配置import javax.servlet.annotation.WebServlet; WebServlet 使用WebServlet注解标注 WebServlet的…

五子棋的设计与实现

术:Java等摘要:五子棋是一种两人对弈的纯策略型棋类游戏,非常容易上手,老少皆宜。为了更好的推广五子棋,研究简单的人工智能方式,运用Java开发五子棋游戏。主要包含了人机对战,棋盘初始化&#…

中国跻身量子计算第一梯队,为何它是硬科技必争之地?丨两会唠科

科技云报道原创。 “两会唠科”是由腾讯科技推出的两会特别策划,重点讲述中国科技名片。本期与科技云报道联合出品,聚焦中国量子计算研究成果和相关进展。 全球越来越多的国家加入到量子科技领域的竞赛当中,争夺下一步的技术战略制高点。 今…

【论文随笔】Transfer of temporal logic formulas in reinforcement learning

Zhe Xu and Ufuk Topcu. 2019. Transfer of temporal logic formulas in reinforcement learning. In Proceedings of the 28th International Joint Conference on Artificial Intelligence (IJCAI’19). AAAI Press, 4010–4018. 这是一篇将inference和learning结合起来的文章…

Selenium如何隐藏浏览器页面?

Selenium隐藏浏览器页面 背景 在工作,学习中,我们常常会使用selenium来获取网页上的数据,编完完整程序之后,实现真正意义上的自动化获取,此时我们会发现在运行中往往会弹出浏览器页面,在调试过程中&…

Java程序的逻辑控制

一、顺序结构 顺序结构比较简单,如果我们按照代码书写的顺序一行一行执行,将会是这样的: System.out.println("aaa"); System.out.println("bbb"); System.out.println("ccc"); // 运行结果 aaa bbb ccc 如…