【JAVA】经典面试题:HashMap,Hashtable和ConcurrentHashMap三者之间的区别!!!

news2025/1/18 10:05:53

本篇的内容是围绕哈希表来展开的,主要是通对HashMap,Hashtable,ConcurrentHashMap三者的特点去了解这它们之间的区别以及运用场景

目录

1. HashMap

2. Hashtable

锁太粗问题:

3. 扩容机制问题

3. ConcurrentHashMap

ConcurrentHashMap做出的优化(以JDK8为例)

1. 读操作优化:

2. 锁的粗细优化:

3. 扩容机制优化(化整为零):

4.三者区别




 

1. HashMap

HashMap是继承与Map集合类的,其内部的方法是没有对线程安全进行处理的,所以HashMap是线程不安全的,在当今高并发的时代需要处理线程安全问题显然这种场景再使用HashMap就已经不太合适了。

所以针对多线程的开发环境就有了Hashtable和ConcurrentHashMap这两个线程安全的HashMap

2. Hashtable

Hashtable类做出的主要是针对HashMap类的主要的方法进行简单的加锁,用synchronized关键字对主要的方法进行修饰保证线程安全

原码:

put方法:

remove方法:

get方法:

 

优点:弥补了普通HashMap不能保证线程安全的缺点

缺点: 

锁太粗问题:

通过原码我们看到为了保证map的线程安全只是给方法直接加synchronized关键字,但是这样的操作显然效率很低;

1. 因为每一个synchronized的锁对象都是this,都是调用方法的对象本身,此时在多线程下不管同时进行任何加锁操作都需要串行执行,就算是通过get多线程操作拿value值也是串行执行的,但本身只拿的话其实并不涉及到线程安全,白白损耗效率。

2. 我们都知道每个key的哈希桶里的值都互不相关,比如key为1的元素和key为2的元素分别在不同的哈希桶里本身就是分开的,此时我们同时对不同key关键字的桶里put元素也不换有线程安全问题,但是Hashtable每个方法的锁对象都是this,所以即使互不相关也会产生锁冲突串行执行,白白损耗效率

图:

3. 扩容机制问题

Hashtable 的扩容机制是,当负载因子达到扩容条件时,那么该线程就会重新创建一个新map再把原map中的所有元素都一次性的全部拷贝到新map上然后在进行替换,这个过程涉及到大量元素拷贝过程,也是效率很低

当map太大且进行到当前扩容操作时,此时此次操作就会花费很长时间,影响使用

3. ConcurrentHashMap

ConcurrentHashMap 也是一种线程安全的哈希表,并且在老大哥Hashtable的基础上做出了很多优化,现在ConcurrentHashMap已经被广泛使用在高并发的场景下了

图:

ConcurrentHashMap做出的优化(以JDK8为例)

1. 读操作优化:

读操作取消了加锁操作,但是使用了volatile关键字保证了内存的可见性,即使其他线程更改了数据,也能感知到

2. 锁的粗细优化:

ConcurrentHashMap让key的哈希桶都有一个单独的锁来控制,这个锁是当前当前key的第一个元素,可以保证多线程下每次对该桶进行操作加锁的都是这个锁对象,保证操作的线程安全,且桶与桶之间的操作由于是不同锁对象控制所以不存在锁竞争,大大提高了并发的程度

3. 扩容机制优化(化整为零):

参考分摊的思想理解:

哈希表在负载因子过高导致扩容时,一次性把所以元素拷贝到位的效率太低了,可能会导致当前步骤导致时间卡顿,由于哈希表的put和get等操作都是O(1),只是扩容这一步操作消耗的时间很多,那么把扩容这个步骤拆分为多个小块,分摊到每次操作的步骤中

ConcurrentHashMap就不会一次性直接把所有元素拷贝到位,而是先创建好新的map,再拷贝一小部分元素到新的map里,在后续每次操作map时都拷贝一部分元素到新的map中,直到老的map全部被拷贝完,再舍弃老的map,只保留新的map

细节:那么在扩容期间就存在新老两个map了,进行put操作时会直接往新的map中放,删除和查找则是都是在新老两个map中找到元素        

4.三者区别

HashMap:多线程使用的环境下存在线程安全问题

Hashtable: 保证了线程安全,但是由于所有方法都同用一把锁(this)导致锁的粒度过粗,没有很好的并发效果,且在扩容时效率低下

ConcurrentHashMap:保证了线程安全,且在Hashtable上做出了很多优化(介绍主要的几点)

1. 每个桶的锁进行了分离,分别控制加锁解锁

2. 取消了读加锁的机制,通过使用volatile关键字保证了内存可见性

3. 扩容是采用了分摊,不在让这一次的高消耗一次性执行完,而是分摊到后续的操作中,进而抵消时间损耗的峰值,让每次操作都变的效率比较高


本篇文章介绍到这里就结束了,欢迎各位友友的补充和纠错!

 

 

 

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

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

相关文章

N5183B信号发生器

N5183B N5183B,是德keysight N5183B 主要特性与技术指标信号特征9 kHz &#xff5e; 3 或 6 GHz在 3 GHz 时提供 24 dBm 功率&#xff0c;带有电子衰减器1 GHz 和 20 kHz 偏置时&#xff0c;相位噪声为 -146 dBc≤-73 dBc ACP W-CDMA 64 DPCH 和 <0.4% EVM 160 MHz 802.11…

万字长文解读Stable Diffusion的核心插件—ControlNet

目录 一、介绍 二、使用方法 三、ControlNet结构 1.整体结构 2.ControlLDM 3.Timestep Embedding 4.HintBlock 5.ResBlock 6.SpatialTransformer 7.SD Encoder Block 8.SD Decoder Block 9.ControlNet Encoder Block 10.Stable Diffusion 四、训练 1.准备数据集…

stable-diffusion-webui浅叙

GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI 使用Git下载&#xff1a; git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git 运行 webui-user.bat : git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.g…

【NestJs】使用MySQL创建多个实体

如果小伙伴还不会使用nestjs连接数据库的话 可以看我的上一篇文章 NestJs使用连接mysql企业级开发规范 关系 关系是指两个或多个表之间的联系。关系基于每个表中的常规字段&#xff0c;通常包含主键和外键。关系有三种&#xff1a; 名称说明一对一主表中的每一行在外部表中有…

从零到一发布 NPM 包

如果你负责前端的基础能力建设&#xff0c;发布各种功能/插件包犹如家常便饭&#xff0c;所以熟悉对 npm 包的发布与管理是非常有必要的&#xff0c;故此有了本篇总结文章。本篇文章一方面总结&#xff0c;一方面向社区贡献开箱即用的 npm 开发、编译、发布、调试模板&#xff…

【展会邀请】百华与您相约第104届中国劳动保护用品交易会!

重磅消息&#xff01;一场行业极具规模的劳保展 第104届中国劳动保护用品交易会 暨2023中国国际职业安全及健康产业博览会 将于2023.4.13-15在上海新国际博览中心E1-E7馆隆重举办&#xff01; 山东百华鞋业有限公司受邀参展&#xff0c;正在火热筹备中。 百华展位号 2023…

算法:将一个数组旋转k步

题目 输入一个数组如 [1,2,3,4,5,6,7]&#xff0c;输出旋转 k 步后的数组。 旋转 1 步&#xff1a;就是把尾部的 7 放在数组头部前面&#xff0c;也就是 [7,1,2,3,4,5,6]旋转 2 步&#xff1a;就是把尾部的 6 放在数组头部前面&#xff0c;也就是 [6,7,1,2,3,4,5]… 思路 思…

PasteSpider的下载和安装

你是否在纠结于k8s的庞大和复杂&#xff0c;是否在被混论的发布流程搞得焦头烂额。PasteSpider适合你&#xff01;足够小的内存资源消耗(300MB甚至更低&#xff01;)&#xff0c;不需要专业的运维知识&#xff0c;图文操作&#xff0c;支持一键发布&#xff0c;支持自动路由配置…

泛型基本说明

使用传统方法的问题分析 不能对加入到集合ArrayList中的数据类型进行约束&#xff08;不安全&#xff09;遍历的时候&#xff0c;需要进行类型转换&#xff0c;如果集合中的数据量较大&#xff0c;对效率有影响。泛型的好处 编译时&#xff0c;检查添加元素的类型&#xff0c;提…

springbean 的 setter/构造注入

文章目录前言一、另外两种注入的怎么用&#xff1f;二、使用setter和构造注入的步骤1. 搞一个配置类,用户获取spring容器中的bean2. 由于有静态方法,所以直接调用三、使用final 的构造注入方式(推荐)总结前言 我们知道,一般java中的依赖注入有三种: 1 属性注入 2 settter注入 …

Golang每日一练(leetDay0039) 二叉树专题(8)

目录 115. 不同的子序列 Distinct Subsequences &#x1f31f;&#x1f31f;&#x1f31f; 116. 填充每个节点的下一个右侧节点指针 Populating-next-right-pointers-in-each-node &#x1f31f;&#x1f31f; 117. 填充每个节点的下一个右侧节点指针 II Populating-next-ri…

模拟信号放大转换器 非隔离 线性对应输入输出 大功率负载

概述&#xff1a; 导轨安装DIN11 NIPO 系列模拟信号放大器是一种将输入信号放大、转换成按比例输出的直流信号放大器。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等需要直流信号测控的行业。此系列产品内部采用稳压电路&#xff0c;通过等比例控制线性放大输…

Threshold ECDSA——web3.0开发中的门限签名

多重签名 1.联名账户&#xff0c;任何一个密钥都能打开账户。 2.储蓄账户&#xff0c;需要所有密钥才能打开账户。 3.级联账户&#xff0c;可以使用部分密钥做部分功能&#xff0c;需要所有密钥才能执行全部功能。 4.在加密货币中&#xff0c;多重签名通过创建一个多重签名…

超详细从入门到精通,pytest自动化测试框架实战-fixture多样玩法(九)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在编写测试用例&…

uniapp开发小程序:使用webview 跳转外部链接

一、使用uniapp开发小程序时&#xff0c;要跳转外部链接&#xff0c;实现的效果如下&#xff1a; 二、实现的步骤&#xff1a; ①先在自己uniapp项目pages.json中建一个页面webview.vue {"path" : "pages/webview/webview","style" : …

(详细)带你运行ShanghaiTech数据集预训练模型——人群计数算法

写在前面 ShanghaiTech数据集是现有的人群计数领域比较常用的训练测试数据集&#xff0c;该博客将详细讲述如何跑通ShanghaiTech官方提供的预训练版本。 由于全部跑完ShanghaiTech数据集需要一定的时间&#xff0c;今天只以其中的ped2数据集&#xff08;体量最小&#xff09;…

linux_回收子进程(何为孤儿进程、僵尸进程、wait函数、waitpid函数)

接上一篇&#xff1a;linux_exec函数族-execl函数-execlp函数-execle函数-execv函数-execvp函数-execve函数 今天来向大家分享几个有趣的知识点&#xff0c;一个是孤儿进程&#xff0c;一个是僵尸进程&#xff0c;hhh&#xff0c;是不是很有趣&#xff0c;另外再来看看怎么去回…

Kubernetes集群调度增强之超容量扩容

作者&#xff1a;京东科技 徐宪章 1 什么是超容量扩容 超容量扩容功能&#xff0c;是指预先调度一定数量的工作节点&#xff0c;当业务高峰期或者集群整体负载较高时&#xff0c;可以使应用不必等待集群工作节点扩容&#xff0c;从而迅速完成应用横向扩容。通常情况下HPA、Cl…

链表与邻接表|栈与队列|kmp

目录 单链表&#xff08;邻接表&#xff09; 双链表 数组模拟栈、队列 单调栈 单调队列&#xff08;滑动窗口&#xff09; KMP 一、KMP算法基本概念与核心思想 二、next数组的含义 三、匹配的思路 四、求next数组 单链表&#xff08;邻接表&#xff09; #include &…

Linux操作基础(系统安全及应用)

文章目录一 、账号安全基本措施1.1 系统账号清理1.2 密码安全控制1.21 设置密码有效期1.3 命令历史限制1.31 修改history命令条数1.32 清空history的方式1.33 设置终端自动注销二 、使用su命令切换用户2.1 限制使用su命令切换用户2.2 sudo命令—提升执行权限三 、系统引导和登录…