线程安全-5 ConcurrentHashMap

一.HashMap为什么是线程不安全的?

1.非同步操作:HashMap的操作不是线程同步的,多线程时会出现可见性问题。

2.非原子操作:HashMap的操作是非原子性的,多线程时会导致原子性问题。

(1)put()操作

        a.jdk1.7的HashMap的put()操作,采用了链表头插法。多线程情况下,在扩容时可能会出现链表死循环的问题。

        b.jdk1.8的HashMap的put()操作,虽然改用链表尾插法避免了死循环的问题,但仍会因为非原子操作而产生问题:

                问题1:发生哈希冲突本该以链表形式存储冲突的数据,但在多线程情况下,可能会由于线程上下文切换而导致忽视了哈希冲突,出现值覆盖的情况。

                问题2:put()操作在添加完元素后,还会对size变量进行修改,由于该操作不是原子性的,同样会引发多线程对size的修改产生冲突的情况。

(2)resize()操作:resize()操作涉及到了对节点的复制和重定位,若非原子操作,则在扩容期间可能会有其他线程对数组进行并发修改,产生问题。

3.HashTable是线程安全的,但其线程安全的实现是通过在方法上加synchronized锁,性能低下。

二.说一下jdk1.7和jdk1.8的ConcurrentHashMap的区别

1.数据结构不同

(1)jdk1.7的ConcurrentHashMap使用segment数组和HashEntry数组实现

        a.segment[]:大数组,将HashMap的table数组切分成若干段小数组HashEntry,segment数组的每个元素就对应一段HashEntry。

        b.HashEntry[]:小数组,采用数组+链表的形式存储数据节点。

(2)jdk1.8的ConcurrentHashMap使用与HashMap一样的结构:数组+链表/红黑树

(3)jdk1.7的ConcurrentHashMap访问数据需要进行两次定位,第一次定segment元素位置,第二次定HashEntry元素未知;而jdk1.8只需要进行一次定位。

2.加锁方式不同

(1)jdk1.7的ConcurrentHashMap的加锁对象是segment数组的一个元素,即一次锁一整段HashEntry,用的是可重入锁ReentrantLock。

(2)jdk1.8的ConcurrentHashMap的加锁对象是当前槽位的头节点,一次只锁一个槽位,用的是synchronized锁。

(3)jdk1.8用synchronized锁一个槽位,相比于jdk1.7用ReentrantLock锁一整段HashEntry,分段粒度更细,性能更好。

三.jdk1.8的ConcurrentHashMap是如何保证线程安全的?

1.table数组用volatile关键字修饰,保证了可见性和有序性。

2.put()操作

在最外层加一个乐观锁,或者说是一个for死循环,通过这个循环来完成多层条件逻辑的执行:

(1)判断table数组是否为null或长度为0,若满足则初始化数组,并进入下一次循环

(2)通过索引公式定位到目标槽位,如果目标槽位为空,则通过CAS操作插入数据

        a.若CAS操作成功,则退出循环

        b.若CAS操作失败,说明有其他线程修改,进入下一次循环

(3)判断当前数组是否在扩容,若满足则帮助数组扩容。无论是否在扩容,都会进入下一次循环

(4)无论是CAS操作失败还是目标槽位已有数据,都会走到当前判断逻辑,即说明当前槽位已经被占有。因此接下来执行判断,若满足以下条件:不需要覆盖原值、槽位节点的hash与新节点的hash相同、槽位节点的key与新节点的key相同,则直接将当前槽位节点的值返回;否则进入下一次循环

(5)为当前槽位的头节点申请synchronized锁,申请到锁后,执行HashMap的添加逻辑。

退出乐观锁后会进行判断:是否要扩容或链表转红黑树,若满足则执行对应逻辑。

上述五个条件,乐观锁的每次循环完成一个条件的处理,每多执行一次循环,则往下推进一个条件判断。

put()操作基于CAS操作和synchronized锁保证线程安全且只有在发生哈希冲突时才需要加synchronized锁,降低了锁的粒度,提高了效率。

3.get()操作:由于table数组已经被volatile关键字修饰,因此get()操作无需加锁。

四.对于ConcurrentHashMap,在执行put()操作时,为什么key和value不能为null?

为了防止二义性。

1.对于HashMap:用于单线程环境

(1)在put()时允许key和value为null。key为null则直接映射到0号槽位,value为null则直接存一个null值。

(2)如果get()方法得到一个null值,为了判断是因为不存在这个key而返回一个null还是本来就存了一个null,可以调用containsKey()判断是否存在key,从而解决二义性问题。

2.对于ConcurrentHashMap:用于多线程环境

(1)在put()时如果key或value为null,则直接抛出NullPointException。

(2)如果get()方法得到一个null值,是无法通过containsKey()来解决二义性问题的,因为在此期间可能会有其他线程对该槽位进行并发修改,从而导致判断结果与实际结果出现差异。

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

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

相关文章

python fstring教程(f-string教程)(python3.6+格式化字符串方法)

文章目录 Python F-String 教程:深度探究与实用指南引言基础用法什么是F-String?表达式嵌入 格式化选项小数点精度宽度与对齐数字格式化 高级用法复杂表达式调用函数多行F-String嵌套格式化 总结 Python F-String 教程:深度探究与实用指南 引言 在Pyt…

Python库之PyQuery的高级用法深度解析

Python库之PyQuery的高级用法深度解析 引言 PyQuery是一个强大的Python库,它提供了类似于jQuery的语法来解析和操作HTML和XML文档。虽然PyQuery的基本用法已经相当直观,但本文将深入探讨一些高级用法,帮助开发者更高效地处理复杂的HTML文档…

中间件是什么?信创中间件有哪些牌子?哪家好用?

当今社会,中间件的重要性日益凸显,尤其是在信创背景下,选择适合的中间件产品对于推动企业数字化转型和升级具有重要意义。今天我们就来聊聊中间件是什么?信创中间件有哪些牌子?哪家好用?仅供参考哈&#xf…

深入理解 Python 中的 `os.walk()`

在处理文件系统时,我们经常需要遍历目录结构以查找特定文件或执行某些操作。Python 提供了一个非常有用的函数 os.walk(),它可以帮助我们轻松地遍历目录树。本文将详细介绍 os.walk() 的使用,并提供一个实际的应用示例。 os.walk() 的基本用…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十二)- 微服务(2)

目录 4. Ribbon负载均衡 4.1 负载均衡流程 4.2 负载均衡策略 4.3 Ribbon饥饿加载 5. Nacos注册中心 5.1 服务注册到nacos 5.2 nacos服务分级存储模型 5.3 根据权重负载均衡 5.4 环境隔离--namespace 4. Ribbon负载均衡 4.1 负载均衡流程 4.2 负载均衡策略 默认实现是…

【OpenGL第一个程序】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、OpenGL第一个程序 前言 本文介绍了OpenGL入门的第一个程序,有详细的注释,便于大家理解其中的逻辑。 一、OpenGL第一个程序 #inclu…

加工工件管口倒角在0.00~0.01mm

对于加工工件管口倒角在0.00~0.01mm这样极小尺寸范围内的测量,常规的测量工具和方法可能难以满足精度要求。这种微小倒角的测量通常需要采用高精度的检测技术和设备,以下是一些适用的测量方法:1. 光学干涉仪:利用光波干涉原理&…

cesium绘制区域编辑

npm 安装也是可以的 #默认安装最新的 yarn add cesium#卸载插件 yarn remove cesium#安装指定版本的 yarn add cesium1.96.0#安装指定版本到测试环境 yarn add cesium1.96.0 -D yarn install turf/turf <template><div id"cesiumContainer"></div&…

大学理科生搜题软件?分享四个软件和公众号,来对比看看吧 #笔记#知识分享

在快节奏的大学生活中&#xff0c;合理利用这些日常学习工具&#xff0c;能够让你事半功倍&#xff0c;提高学习效率。 1.福昕翻译 可以一键翻译文档内容&#xff0c;并提供还原排版的译文&#xff0c;对经常看外文文献的朋友来说&#xff0c;绝对是福音 福昕翻译是一流专业…

微信小程序全栈开发实践教程

掌握小程序开发&#xff1a;一站式项目实操指南 课程旨在系统地介绍小程序开发中常用的组件与接口&#xff0c;并强调最佳实践与常见错误的讲解。为确保学习效果&#xff0c;课程将围绕一个大型实战项目展开&#xff0c;该项目综合了小程序开发的关键技术和知识点。通过循序渐…

设计模式基础——设计原则介绍

1.概述 ​ 对于面向对象软件系统的设计而言&#xff0c;如何同时提高一个软件系统的可维护性、可复用性、可拓展性是面向对象设计需要解决的核心问题之一。面向对象设计原则应运而生&#xff0c;这些原则你会在设计模式中找到它们的影子&#xff0c;也是设计模式的基础。往往判…

HTML大雪纷飞

目录 写在前面 HTML简介 完整代码 代码分析 运行结果 系列文章 写在后面 写在前面 小编又又又出现啦&#xff01;这次小编给大家带来大雪纷飞HTML版&#xff0c;不需要任何的环境&#xff0c;只要有一个浏览器&#xff0c;就可以随时随地下一场大雪哦&#xff01; HTM…

Diffusion Model, Stable Diffusion, Stable Diffusion XL 详解

文章目录 Diffusion Model生成模型DDPM概述向前扩散过程前向扩散的逐步过程前向扩散的整体过程 反向去噪过程网络结构训练和推理过程训练过程推理过程优化目标 详细数学推导数学基础向前扩散过程反向去噪过程 Stable Diffusion组成结构运行流程网络结构变分自编码器 (VAE)文本编…

富港银行 邀请码 兑换码 优惠码 分享

首次记得一定要扫码注册&#xff0c;扫码注册开户费50美金&#xff0c;每笔26美金手续费&#xff0c;目前能接收CBI银行资金的有&#xff1a;工行、交通、中行&#xff0c;请知悉 cbi帐户管理费&#xff1a;10美元/月&#xff0c;余额>500美元&#xff0c;1美元/月/&#x…

521源码-免费源码下载-免费网络教程如何在PHP中实现跳转并携带POST数据

PHP是一种流行的服务器端脚本语言&#xff0c;用于构建动态的Web应用程序和网站。在PHP中&#xff0c;经常需要进行页面跳转以及跨页面传输数据。本文将讨论如何在PHP中实现跳转并携带POST数据。 要理解如何在PHP中跳转并携带POST数据&#xff0c;首先需要了解HTTP请求是如何工…

Windows Presentation Foundation(WPF)要点总结

Windows Presentation Foundation&#xff08;WPF&#xff09;是微软推出的一种用于构建Windows桌面应用程序的框架。自从WPF在.NET Framework 3.0中引入以来&#xff0c;它以其强大的功能和灵活性&#xff0c;逐渐成为开发人员构建现代、富用户界面应用程序的首选。本文将概述…

在vue中实现下载文件功能

实际操作为&#xff0c;在表格中 我们可以获取到文件的id&#xff0c;通过插槽就可以实现 <template #default"scope"><el-button type"text" click"handleDown(scope.row)"><span>下载</span></el-button> </…

数组基础-笔记

数组是非常基础的数据结构&#xff0c;实现运用和理解是两回事 数组是存放在连续内存空间上的相同类型的数据的集合 可以方便的通过下表索引的方式获取到下标下对应的数据。 举一个字符数组的例子&#xff1a; 注意两点&#xff1a; 数组下标从0开始 数组内存空间的地址是连…

Python的selenium爬取

1.selenium 1.1.前言 使用python的requests模块还是存在很大的局限性&#xff0c;例如&#xff1a;只发一次请求&#xff1b;针对ajax动态加载的网页则无法获取数据等等问题。特此&#xff0c;本章节将通过selenium模拟浏览器来完成更高级的爬虫抓取任务。 1.2.什么是seleniu…

运维开发详解

运维开发&#xff0c;简单来说&#xff0c;就是维护和优化计算机系统、网络以及软件应用正常运行的工作&#xff0c;同时通过编写代码来自动化这些维护任务&#xff0c;提高效率。想象一下&#xff0c;运维就像是照顾一盆植物&#xff0c;你要浇水、施肥、修剪&#xff0c;保证…