java并发-ConcurrentHashMap 在Java7 和 8 的区别

文章目录

  • 1.Java 7 版本的 ConcurrentHashMap
  • 2.Java 8 版本的 ConcurrentHashMap
  • 3.分析 Java 8 版本的 ConcurrentHashMap 的重要源码
    • 3.1.Node 节点
    • 3.2.put 方法源码分析
    • 3.3.get 方法源码分析
  • 4.对比 Java7 和 Java8 的异同和优缺点
    • 4.1.并发度
    • 4.2.保证并发安全的原理
    • 4.3.遇到 Hash 碰撞
    • 4.4.查询时间复杂度


1.Java 7 版本的 ConcurrentHashMap

我们首先来看一下 Java 7 版本中的 ConcurrentHashMap 的结构示意图:
在这里插入图片描述

从图中我们可以看出,在 ConcurrentHashMap 内部进行了 Segment 分段, Segment 继承了ReentrantLock,可以理解为一把锁,各个 Segment 之间都是相互独立上锁的,互不影响。相比于之前的Hashtable每次操作都需要把整个对象锁住而言,大大提高了并发效率。因为它的锁与锁之间是独立的,而不是整个对象只有一把锁。
每个 Segment 的底层数据结构与 HashMap 类似,仍然是数组和链表组成的拉链法结构。默认有 0~15共 16Segment,所以最多可以同时支持 16 个线程并发操作(操作分别分布在不同的 Segment 上)。 16 这个默认值可以在初始化的时候设置为其他值,但是一旦确认初始化以后,是不可以扩容 的。

2.Java 8 版本的 ConcurrentHashMap

在 Java 8 中的示意图:

在这里插入图片描述

图中的节点有三种类型。

  • 第一种是最简单的,空着的位置代表当前还没有元素来填充。
  • 第二种就是和 HashMap 非常类似的拉链法结构,在每一个槽中会首先填入第一个节点,但是后续 如果计算出相同的 Hash 值,就用链表的形式往后进行延伸。
  • 第三种结构就是红黑树结构,这是 Java 7 的 ConcurrentHashMap 中所没有的结构。

当第二种情况的链表长度大于某一个阈值(默认为 8),且同时满足一定的容量要求的时候,ConcurrentHashMap 便会把这个链表从链表的形式转化为红黑树的形式,目的是进一步提高它的查找性能。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色黑色,是一种平衡二叉查找树,查找效率高,会自动平衡,防止极端不平衡从而影响查找效率的情况发生。由于自平衡的特点,即左右子树高度几乎一致,所以其查找性能近似于二分查找,时间复杂度是O(log(n))级别;反观链表,它的时间复杂度就不一样了,如果发生了最坏的情况,可能需要遍历整个链表才能找到目标元素,时间复杂度为 O(n),远远大于红黑树的 O(log(n)),尤其是在节点越来越多的情况下,O(log(n))体现出的优势会更加明显。
红黑树的一些其他特点:

  • 每个节点要么是红色,要么是黑色,但根节点永远是黑色的。
  • 红色节点不能连续,也就是说,红色节点的子和父都不能是红色的。
  • 从任一节点到其每个叶子节点的路径都包含相同数量的黑色节点。

正是由于这些规则和要求的限制,红黑树保证了较高的查找效率,好处就是避免在极端的情况下冲突链表变得很长,在查询的时 候,效率会非常慢。而红黑树具有自平衡的特点,所以,即便是极端情况下,也可以保证查询效率在O(log(n))

3.分析 Java 8 版本的 ConcurrentHashMap 的重要源码

由于 Java 7 版本已经过时了,所以我们把重点放在 Java 8 版本的源码分析上。

3.1.Node 节点

我们看看最基础的内部存储结构 Node,这就是一个一个的节点,如这段代码所示:

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V val;volatile Node<K,V> next;}

可以看出,每个 Node里面是key-value的形式,并且把 valuevolatile 修饰,以便保证可见性,同时内部还有一个指向下一个节点的 next 指针,方便产生链表结构。
下面我们看两个最重要、最核心的方法。

3.2.put 方法源码分析

put 方法的核心是 putVal 方法,为了方便阅读,我把重要步骤的解读用注释的形式补充在下面的源码中。

final V putVal(K key, V value, boolean onlyIfAbsent) {// 检查是否有空键或值if (key == null || value == null) throw new NullPointerException();// 计算哈希并进行扩散以避免聚集int hash = spread(key.hashCode());int binCount = 0;// 无限循环以处理哈希冲突和表初始化for (Node<K, V>[] tab = table;;) {Node<K, V> f;int n, i, fh;// 检查表是否为空,如果是,则初始化表if (tab == null || (n = tab.length) == 0)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {// 如果桶为空,请尝试原子性地添加新节点if (casTabAt(tab, i, null, new Node<K, V>(hash, key, value, null)))break; // 添加到空桶时不需要锁定} else if ((fh = f.hash) == MOVED)// 如果桶正在转移中,请协助进行转移tab = helpTransfer(tab, f);else {V oldVal = null;// 在桶上同步以处理并发修改synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {// 处理桶中的链表binCount = 1;for (Node<K, V> e = f;; ++binCount) {K ek;if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K, V> pred = e;if ((e = e.next) == null) {// 在链表中添加新节点pred.next = new Node<K, V>(hash, key, value, null);break;}}} else if (f instanceof TreeBin) {// 处理桶中的树节点Node<K, V> p;binCount = 2;if ((p = ((TreeBin<K, V>) f).putTreeVal(hash, key, value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}// 检查是否进行了任何修改并采取适当的操作if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)// 如果链表长度超过阈值,则将链表转换为树treeifyBin(tab, i);if (oldVal != null)// 如果存在旧值,则返回旧值return oldVal;break;}}}// 更新地图中元素的计数addCount(1L, binCount);return null;
}

以下是代码的主要功能和步骤解释:

  1. 参数检查: 首先,代码检查传入的键和值是否为null,如果是,则抛出NullPointerException
  2. 计算哈希: 使用键的哈希码,并通过spread方法对哈希码进行扩散,以减少哈希冲突的可能性。
  3. 循环处理: 进入一个无限循环,用于处理可能的哈希冲突和表的初始化。
  4. 初始化表: 如果哈希表为null或长度为0,则通过调用initTable方法初始化哈希表。
  5. 处理空桶: 如果计算得到的桶是空的,则尝试使用CAS(Compare and Swap)原子操作添加新的Node节点到该桶中,如果成功则退出循环。
  6. 处理转移中的桶: 如果当前桶的状态是MOVED(正在进行桶的迁移操作),则调用helpTransfer方法协助进行桶的迁移。
  7. 处理非空桶: 如果桶非空,说明存在哈希冲突,根据桶的类型(链表或树)采取不同的处理方式。
  • 如果桶中是链表(fh >= 0),则使用同步块在链表中查找键值对。如果找到了相同的键,更新其值,否则将新的Node节点添加到链表中。
  • 如果桶中是树(f instanceof TreeBin),则调用putTreeVal方法在树中插入键值对。
  1. 更新计数: 最后,根据实际添加的节点数更新哈希表中元素的计数。
  2. 返回值: 如果在添加操作中替换了已存在的值,返回被替换的旧值,否则返回null。

3.3.get 方法源码分析

get 方法比较简单,我们同样用源码注释的方式来分析一下:

public V get(Object key) {// 定义局部变量Node<K, V>[] tab;Node<K, V> e, p;int n, eh;K ek;// 计算扩散后的哈希值int h = spread(key.hashCode());// 检查哈希表是否已初始化if ((tab = table) != null && (n = tab.length) > 0 &&(e = tabAt(tab, (n - 1) & h)) != null) {// 遍历链表或树,查找对应的键值对if ((eh = e.hash) == h) {// 判断第一个节点是否匹配if ((ek = e.key) == key || (ek != null && key.equals(ek)))return e.val; // 返回找到的值} else if (eh < 0)return (p = e.find(h, key)) != null ? p.val : null; // 处理树结构// 在链表中遍历查找while ((e = e.next) != null) {if (e.hash == h &&((ek = e.key) == key || (ek != null && key.equals(ek))))return e.val; // 返回找到的值}}// 未找到匹配的键,返回nullreturn null;
}
  1. 计算哈希: 计算传入键的哈希值,并通过spread方法进行扩散。
  2. 检查表和桶: 检查哈希表是否已经初始化,以及哈希桶中是否存在节点。
  3. 处理第一个节点: 检查第一个节点是否匹配,如果匹配则返回对应的值。
  4. 处理树结构: 如果哈希桶中是树结构,则调用find方法查找匹配的键值对。
  5. 遍历链表: 在链表中遍历查找匹配的键值对,如果找到则返回对应的值。
  6. 未找到匹配: 如果遍历完仍未找到匹配的键,则返回null。

4.对比 Java7 和 Java8 的异同和优缺点

数据结构
Java 7 采用 Segment 分段锁来实现,而 Java 8 中的 ConcurrentHashMap 使用数组 + 链表 + 红黑树。

在这里插入图片描述

4.1.并发度

Java 7 中,每个 Segment 独立加锁,最大并发个数就是 Segment 的个数,默认是 16
Java 8 中,锁粒度更细,理想情况下 table 数组元素的个数(也就是数组长度)就是其支持并
发的最大个数,并发度比之前有提高。

4.2.保证并发安全的原理

Java 7 采用 Segment 分段锁来保证安全,而 Segment 是继承自 ReentrantLock
Java 8 中放弃了 Segment 的设计,采用 Node+CAS+synchronized 保证线程安全。

4.3.遇到 Hash 碰撞

Java 7 在 Hash 冲突时,会使用拉链法,也就是链表的形式。
Java 8 先使用拉链法,在链表长度超过一定阈值时,将链表转换为红黑树,来提高查找效率。

4.4.查询时间复杂度

Java 7 遍历链表的时间复杂度是O(n),n 为链表长度。
Java 8 如果变成遍历红黑树,那么时间复杂度降低为 O(log(n)),n 为树的节点个数。

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

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

相关文章

Jmeter实现CSV数据批量导入

CSV&#xff1a;逗号分隔值&#xff0c;是一种简洁且常见的数据存储格式。 1、参数化&#xff1a; 在Jmeter中&#xff0c;可以通过“用户自定义的变量”来实现参数化使操作方便&#xff0c;使用语法位&#xff1a;${参数名}&#xff0c;如下图&#xff1a; 而CSV也同理&…

本地文件内容搜索神器AnyTXT Searcher如何搭建与远程访问

文章目录 前言1. AnyTXT Searcher1.1 下载安装AnyTXT Searcher 2. 下载安装注册cpolar3. AnyTXT Searcher设置和操作3.1 AnyTXT结合cpolar—公网访问搜索神器3.2 公网访问测试 4. 固定连接公网地址 前言 你是否遇到过这种情况&#xff0c;异地办公或者不在公司&#xff0c;想找…

java注意项--更新中

前言&#xff1a; 1.大小写规定 1.1.类名和接口名&#xff1a;每个单词首字母大写。如GoodStudent&#xff1b; 是一个单词的时候首字母大写。如Student&#xff1b; 1.2.变量和方法名&#xff1a;第一个首字母小写&#xff0c;后序首字母大写。如firstName&#xff1b; 是一…

vue的语法模板与数据绑定的说明

vue的两大模板语法&#xff1a; 1.插值语法 2.指定语法 插值语法&#xff1a;{{}} 功能&#xff1a;用于解析标签体的内容 写法&#xff1a;{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性 指定语法&#xff1a; 功能:用于解析标签(包括:标签属性、标…

ChatGPT助力Excel数据分析:让你的工作事半功倍!

文章目录 一、ChatGPT简介二、ChatGPT在Excel数据分析中的应用1. 数据清洗2. 数据处理3. 数据分析4. 数据可视化 三、如何使用ChatGPT进行Excel数据分析1. 安装ChatGPT插件2. 输入问题或命令3. 查看结果并调整参数4. 导出结果并分享四、总结与展望 《巧用ChatGPT高效搞定Excel数…

苹果cms论坛多播放源自动采集 /采集在线影视网站/苹果CMS影视站采集器

源码介绍&#xff1a; 苹果cms论坛多播放源自动采集、采集在线影视网站&#xff0c;作为苹果CMS影视站采集器&#xff0c;它能轻松获取在线影视网站资源。 苹果 cms 论坛这是一个基于Vue和Gin实现的在线观影网站。项目采用 vite vue 作为前端技术栈, 使用 ElementPlus 作为 …

el-select 全选

<template><div class"container"><el-selectv-model"choosedList"clearablemultiplecollapse-tagsplaceholder"请选择"change"select_Change"><div style"padding: 0 20px; line-height: 34px">&l…

JVM快速入门

JVM 字节码 字节码文件的组成 字节码由五个部分组成&#xff1a;基础信息 常量池 字段 方法 属性 基础信息&#xff1a; 魔数、字节码文件对应的版本号、访问标识&#xff08;public final&#xff09;、该类的父类索引、该类实现哪些接口的索引 魔数&#xff1a;文件无法…

顶级加密混淆混淆工具测评:ipagurd

摘要 JavaScript代码安全需求日益增长&#xff0c;因此JavaScript混淆工具的使用变得广泛。本文将对专业、商业JavaScript混淆工具ipagurd进行全面评估&#xff0c;通过比较其功能、操作便捷性、免费试用、混淆效果等方面&#xff0c;帮助开发者选择适合自己项目需求的工具。 …

期货平仓日历(期货平仓日期汇总)

什么是期货平仓日历&#xff1f; 期货是一种高风险高收益的投资品种。而期货交易不同于股票等其他投资品种的交易&#xff0c;期货交易需要在一定时间内才能买卖。而期货平仓日历就是指期货交易中规定的所有合约的平仓日期汇总。 常见期货平仓日期和时间&#xff1f; 不同的…

关于EasyExcel 合并单元格方法该如何实现

在做一个业务的导出&#xff0c;目前遇到一个需求&#xff0c;如下图&#xff1a; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metad…

在mt5上哪里可以添加指数品种?

在MT5交易平台上&#xff0c;您可以通过以下步骤添加指数品种&#xff08;如股票指数、商品指数等&#xff09;到您的市场观察窗口中&#xff1a; Exness手机登录平台学习指南 步骤一&#xff1a;打开市场观察窗口&#xff1a; 打开MT5交易平台。 在左侧的“市场观察”窗口中&…

高集成高能效FAN21SV04MPX 单输入集成同步降压调节器技术解析

FAN21SV04MPX 是一款高效、小型、可编程频率的 4 A 集成同步降压调节器。FAN21SV04MPX 采用经过优化的互联方式将同步MOSFET和控制器/驱动器包含在一个封装中&#xff0c;使得设计人员能够使用最少的外部元件&#xff0c;在较小面积中满足高电流要求&#xff0c;从而降低成本。…

利用Spark构建房价分析与推荐系统:基于58同城数据的大数据实践

利用Spark构建房价分析与推荐系统&#xff1a;基于58同城数据的大数据实践 基于Spark的房价数据分析预测推荐系统引言技术栈功能概述项目实现1. 数据爬取与处理2. 大数据分析与可视化3. 房价预测模型4. 协同过滤推荐系统5. Web应用开发6. 数据管理与用户管理 总结与展望 基于Sp…

docker学习(十一、Redis集群存储数据方式)

文章目录 一、集群数据存储1.单机连接集群问题2.集群方式连接redis存储数据 二、 查看集群信息 docker搭建Redis集群相关知识&#xff1a; docker学习&#xff08;九、分布式存储亿级数据知识&#xff09; docker学习&#xff08;十、搭建redis集群&#xff0c;三主三从&#x…

MAC苹果笔记本电脑如何彻底清理垃圾文件软件?

苹果电脑以其流畅的操作系统和卓越的性能而备受用户喜爱。然而&#xff0c;随着时间的推移&#xff0c;系统可能会积累大量垃圾文件&#xff0c;影响性能。本文将介绍苹果电脑怎么清理垃圾文件的各种方法&#xff0c;以提升系统运行效率。 CleanMyMac X是一款专业的Mac清理软件…

【音视频 | AAC】AAC音频编码详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

re:Invent 2023技术上新|Amazon Bedrock现提供对Anthropic最新模型Claude 2.1访问权限

亚马逊云科技已在 Amazon Bedrock 中推出 Anthropic 的 Claude 2.1 基础模型&#xff08;FM&#xff09;。此前&#xff0c;Anthropic 推出了其最新模型 Claude 2.1&#xff0c;此模型为企业提供了一些关键功能&#xff0c;如业界领先的 200,000 个令牌化上下文窗口&#xff08…

vivado 约束条件效率

约束条件效率 审查约束覆盖范围编写时间约束时&#xff0c;重要的是保持约束的简单性并指定它们仅在相关网表对象上。低效的约束导致更大的运行时间和更大的内存消耗。低效的约束也可能导致设计受到不适当的约束&#xff0c;因为定时异常可能会意外地覆盖比预期更多的路径&…

谈谈微服务的Ribbon知识点

Ribbon负载均衡 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。(负载均衡RestTemplate调用)&#xff0c;在服务调用过程中的负载均衡一般使用SpringCloud的Ribbon 组件实现 , Feign的底层已经自动集成了Ribbon , 使用起来非常简单。 Configuration…