ConcurrentHashMap底层原理?

本文为面试必备系列篇,不深入叙述,具体细节可自行查询。

可能会问的问题

1、用过ConcurrentHashMap吗?
2、为什么要用ConcurrentHashMap?
3、HashMap与HashTable的区别,引出ConcurrentHashMap…
4、HashMap在多线程环境下存在线程安全问题,那你一般都是怎么处理这种情况的?
5、能说一下ConcurrentHashMap是怎么实现的吗?

为什么要用ConcurrentHashMap?

在并发编程中使用HashMap可能会导致程序陷入死循环,而使用线程安全的HashTable效率又非常低,所以采用了ConcurrentHashMap。

单看这个回答,就会牵扯到「为和编发编程中使用HashMap会导致程序陷入死循环?」和「HashTable为何效率低下?」这两个问题,具体可参考上篇 > 面试必备:HashMap底层数据结构?jdk1.8算法优化,hash冲突,扩容等问题

关于ConcurrentHashMap实现原理的两个参考回答,自己可以重新组织一下:

ConcurrentHashMap采用的是分段式锁,与之对应的就是HashTable,HashTable使用的是Synchronize关键字,是对一个大的数组加一把锁,其实是对对象加锁,锁住的是对象整体,性能肯定是比较差的,现在ConcurrentHashMap是将大数组拆分成许多的小数组,每一个小数组拥有一把锁,允许多个修改操作并发进行。

ConcurrentHashMap采用的是分段式锁,可以理解为把一个大的Map拆封成N个小的Segment,在put数据时会根据hash来确定具体存放在哪个Segment中,Segment内部的同步机制是基于Lock操作的,每一个Segment都会分配一把锁,当线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问,也就是实现并发访问。

继续拓展,分段式锁是如何实现的?

ConcurrentHashMap在JDK1.7和JDK1.8之间是有区别的,当然,这个问题也可以这样问:

能说一下ConcurrentHashMap在JDK1.7和JDK1.8中的区别吗?

1、JDK1.7:

HashEntry数组 + Segment数组 + Unsafe 「大量方法运用」

JDK1.7中数据结构是由一个Segment数组和多个HashEntry数组组成的,每一个Segment元素中存储的是HashEntry数组+链表,而且每个Segment均继承自可重入锁ReentrantLock,也就带有了锁的功能,当线程执行put的时候,只锁住对应的那个Segment 对象,对其他的 Segment 的 get put 互不干扰,这样子就提升了效率,做到了线程安全。

额外补充:我们对 ConcurrentHashMap 最关心的地方莫过于如何解决 HashMap 在 put 时候扩容引起的不安全问题?

在 JDK1.7 中 ConcurrentHashMap 在 put 方法中进行了两次 hash 计算去定位数据的存储位置,尽可能的减小哈希冲突的可能行,然后再根据 hash 值以 Unsafe 调用方式,直接获取相应的 Segment,最终将数据添加到容器中是由 segment对象的 put 方法来完成。由于 Segment 对象本身就是一把锁,所以在新增数据的时候,相应的 Segment对象块是被锁住的,其他线程并不能操作这个 Segment 对象,这样就保证了数据的安全性,在扩容时也是这样的,在 JDK1.7 中的 ConcurrentHashMap扩容只是针对 Segment 对象中的 HashEntry 数组进行扩容,还是因为 Segment 对象是一把锁,所以在 rehash 的过程中,其他线程无法对 segment 的 hash 表做操作,这就解决了 HashMap 中 put 数据引起的闭环问题。

2、JDK1.8:

JDK1.7:ReentrantLock+Segment+HashEntry
JDK1.8:Synchronized+CAS+Node+红黑树

JDK1.8屏蔽了JDK1.7中的Segment概念呢,而是直接使用「Node数组+链表+红黑树」的数据结构来实现,并发控制采用 「Synchronized + CAS机制」来确保安全性,为了兼容旧版本保留了Segment的定义,虽然没有任何结构上的作用。

总之JDK1.8中优化了两个部分:

放弃了 HashEntry 结构而是采用了跟 HashMap 结构非常相似的 Node数组 + 链表(链表长度大于8时转成红黑树)的形式

Synchronize替代了ReentrantLock,我们一直固有的思想可能觉得,Synchronize是重量级锁,效率比较低,但为什么要替换掉ReentrantLock呢?

1、随着JDK版本的迭代,本着对Synchronize不放弃的态度,内置的Synchronize变的越来越“轻”了,某些场合比使用API更加灵活。

2、加锁力度的不同,在JDK1.7中加锁的力度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点),也就是1.8中加锁力度更低了,在粗粒度加锁中 ReentrantLock 可能通过 Condition 来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了,所以使用内置的 Synchronize 并不比ReentrantLock效果差。

18年专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。

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

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

相关文章

[js] js操作节点的方法有哪些?

[js] js操作节点的方法有哪些? 创建节点createElement() 创建一个元素节点createTextNode() 创建一个文本节点createAttribute() 创建一个属性节点createComment() 创建一个注释节点插入节点appendChild() 把节点插入到父节点的末尾insertBefore() 把节点插入到父节…

【刷题】洛谷 P2709 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。 输入输出格式 输入格式&…

支付宝支付-当面付之扫码支付「扫码支付」

前言 支付宝支付—沙箱环境使用支付宝支付-支付宝PC端扫码支付支付宝支付-手机浏览器H5支付支付宝支付-当面付之扫码支付「本文」 当面付包含两种支付方式:商品条形码支付 扫码支付 经过前面两篇PC端扫码支付、手机H5支付,我们可以看到一个共同的特点就…

[js] 写一个格式化时间的方法

[js] 写一个格式化时间的方法 function dateToString(date, format yyyy-MM-dd) {const d new Date(date);let result format;const _config {y: d.getFullYear(),M: d.getMonth() 1, // 月d: d.getDate(), // 日h: d.getHours(), // 小时m: d.getMinutes(), // 分s: d.g…

01: awk常用

1.1 awk基本使用 1、找出当前登录用户数量 [rootlocalhost ~]# w14:09:07 up 48 min, 2 users, load average: 0.00, 0.01, 0.06 USER TTY FROM LOGIN IDLE JCPU PCPU WHAT root :0 :0 204月18 ?xdm? 59.24s 0.04s g…

MybatisCodeHelperNew-2.8.1-191-201插件使用

本文测试环境IDEA_2020.1,文中提供了MacOS用户操作截图 1、文件解压后放置plugs插件目录「Windows」 扫码回复「139」 下载后将文件解压,将压缩包内的 MyBatisCodeHelper-Pro 放入 IDEA 安装目录的 plugins 目录。 2、如果你是MacOS用户「MacOS」 同样找…

[js] pjax和ajax的区别是什么?

[js] pjax和ajax的区别是什么? pjax 是一个 jQuery 插件,它通过 ajax 和 pushState 技术提供了极速的(无刷新 ajax 加载)浏览体验,并且保持了真实的地址、网页标题,浏览器的后退(前进&#xff…

Redis简单案例(四) Session的管理

Redis简单案例(四) Session的管理 原文:Redis简单案例(四) Session的管理负载均衡,这应该是一个永恒的话题,也是一个十分重要的话题。毕竟当网站成长到一定程度,访问量自然也是会跟着增长,这个时候, 一般都会对其进行负…

MacOS中Elasticsearch的安装「借助Homebrew」

1、Homebrew 如果你已经安装过Homebrew了,那么你可以跳过这一步,直接进行Elasticsearch安装步骤; Homebrew是一款MacOS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能,强烈推荐安装。 请…

[js] 微信小程序实现轨迹回放,微信原生小程序,基于uniapp的小程序?

[js] 微信小程序实现轨迹回放,微信原生小程序,基于uniapp的小程序? 需要写出轨迹拖动,进度条,开始,暂停,结束功能。 个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易…

负载均衡中使用 Redis 实现共享 Session

最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题。 一、负载均衡 负…

Typora中使用Gitee图床

1、前言 之前好友写了一篇「使用gitee作为图床 ,写markdown自动上传文件」,初衷是由于我一直使用的是Typora来写博客「力推」,但之前的版本都不支持图床功能,现在新版本已经有了图床功能了,赶紧入坑。 本篇环境:MacOS…

[js] 异步请求重试策略有哪些呢?

[js] 异步请求重试策略有哪些呢? 重试次数、重试时间间隔等个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

【NOIP2017模拟6.25】小W的动漫

题目 小W最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲&#xff…

用Elasticsearch代替数据库存储日志方式

之前的项目中一直使用的是数据库表记录用户操作日志的,但随着时间的推移,数据库log单表是越来越大「不考虑删除」,再加上近期项目中需要用到Elasticsearch,所以干脆把这些用户日志迁移到ES上来了。 环境:SpringBoot2.2…

[js] 写一个方法实现promise失败后自动重试

[js] 写一个方法实现promise失败后自动重试 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&…

jacky解读麻省理工《计算机科学与Python编程导论》第1集

文&#xff1a;数据分析&#xff0d;jacky(朱元禄) &#xff08;一&#xff09;导言 本课程讲的中心思想就是五个字&#xff1a;计算机思维Python只是辅助工具&#xff0c;是辅助大家理解计算机思维&#xff0c;仅此而已 急功近利是人性&#xff0c;适得其反是结果&#xff1a;…

如何理解Java中的自动拆箱和自动装箱?

小伟刚毕业时面的第一家公司就被面试官给问住了&#xff0c;记忆尤深啊… 如何理解Java中的自动拆箱和自动装箱&#xff1f; 自动拆箱&#xff1f;自动装箱&#xff1f;什么鬼&#xff0c;听都没听过啊&#xff0c;这…这…知识盲区… 回到家后小伟赶紧查资料&#xff0c;我…

[js] 实现多张图片合成一张的效果

[js] 实现多张图片合成一张的效果 原理是使用canvas画布。在页面加载前 mounted方法里&#xff1a;setTimeout(() > {this.changeimg();}, 1000);changeimg( )方法&#xff1a;changeimg(){var self this;var imgsrcArray [require(../../assets/image/income/background…

chrome浏览器中解决embed标签 loop=true 背景音乐无法循环的问题。

今天试了下在html网页中加入背景音乐并设置为循环播放。一开始用<embed>标签&#xff0c;设置loop"true", 但是结果发现在IE浏览器可以&#xff0c;但是在chrome浏览器却无法实现循环&#xff0c; 播放完一次自动停止了。代码如下&#xff1a; <embed src&q…