jdk 1.8 ConcurrentHashMap和1.7 ConcurrentHashMap区别 原理解析

本文对比分析了JDK 1.7和1.8中ConcurrentHashMap的区别,1.7使用Segment数组和HashEntry,1.8取消Segment,采用CAS和synchronized,优化了锁的粒度。1.8引入了红黑树提高查询性能,并详细解释了其数据结构和操作流程。同时,文章讨论了ConcurrentHashMap与HashTable的差异和性能优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@[TOC](`jdk 1.8 ConcurrentHashMap和1.7 ConcurrentHashMap区别 原理解析)
#在上一篇文章中,我们详细链接了HashMap在1.7和1.8和实现区别,但是我们都知道HashMap在多线程下是不安全的,所以jdk也为我们提供了并发安全的容器进行使用,那就是我们这篇文章的主角:ConcurrentHashMap。

part1:1.7ConcurrentHashMap探索

我们首先看一下内部结构

在这里插入图片描述
从源码我们了解到,ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment 是一种可重入锁(ReentrantLock),在 ConcurrentHashMap 里扮演锁的角色;HashEntry 则用于存储键值对数据。一个 ConcurrentHashMap 里包含一Segment 数组。Segment 的结构和 HashMap 类似,是一种数组和链表结构。一Segment 里包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得与它对应的 Segment 锁
在这里插入图片描述

构造方法和初始化:ConcurrentHashMap 初始化方法是通过 initialCapacity、loadFactor 和concurrencyLevel(参数 concurrencyLevel 是用户估计的并发级别,就是说你觉得最
多有多少线程共同修改这个 map,根据这个来确定 Segment 数组的大小concurrencyLevel 默认是 DEFAULT_CONCURRENCY_LEVEL = 16;)等几个参数来初始化 segment 数组、段偏移量 segmentShift、段掩码 segmentMask 和每个 segment里的 HashEntry 数组来实现的
在这里插入图片描述
并发级别可以理解为程序运行时能够同时更新 ConccurentHashMap 且不产
生锁竞争的最大线程数,实际上就是 ConcurrentHashMap 中的分段锁个数,即
Segment[]的数组长度。ConcurrentHashMap 默认的并发度为 16,但用户也可以
在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap 会使用大
于等于该值的最小 2 幂指数作为实际并发度(假如用户设置并发度为 17,实际
并发度则为 32)。如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个 Segment 内的访问会扩散到不同的 Segment 中,CPU cache 命中率会下降,从而引起程序性能下降。segments 数组的长度 ssize 是通过 concurrencyLevel 计算得出的。为了能通过按位与的散列算法来定位 segments 数组的索引,必须保证 segments 数组的长度是 2 的 N 次方(power-of-two size),所以必须计算出一个大于或等于concurrencyLevel 的最小的 2 的 N 次方值来作为 segments 数组的长度

既然 ConcurrentHashMap 使用分段锁 Segment 来保护不同段的数据,那么在插入和获取元素的时候,必须先通过散列算法定位到 Segment。ConcurrentHashMap 会首先使用 Wang/Jenkins hash 的变种算法对元素的hashCode 进行一次再散列。ConcurrentHashMap 完全允许多个读操作并发进行,读操作并不需要加锁。
ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的以及volatile 关键

在这里插入图片描述
get 操作先经过一次再散列,然后使用这个散列值通过散列运算定位到Segment(使用了散列值的高位部分),再通过散列算法定位到 table(使用了散列值的全部)。整个 get 过程,没有加锁,而是通过 volatile 保证 get 总是可以拿到最新值
在这里插入图片描述
ConcurrentHashMap 初始化的时候会初始化第一个槽 segment[0],对于其他
槽,在插入第一个值的时候再进行初

### JDK 1.7 JDK 1.8ConcurrentHashMap区别 #### 数据结构变化 在 JDK 1.7 版本中,`ConcurrentHashMap` 使用分段锁定机制来提高并发性能。整个哈希表被分割成多个独立的部分(Segment),每个 Segment 类似于一个小型的 HashMap,并且有自己独立的锁。这使得不同部分可以并行访问而不会相互阻塞。 而在 JDK 1.8 及之后版本里,为了进一步提升性能减少内存占用,引入了一种新的设计——基于 CAS (Compare And Swap) 操作以及链表转红黑树优化策略。当桶内元素数量超过一定阈值时会自动转换为红黑树形式存储键值对,从而提高了查找速度[^4]。 #### 锁粒度调整 由于采用了更细粒度的数据分区方式,在处理高并发场景下能够显著降低争用率;同时通过无锁算法实现了大部分操作无需加锁即可完成,特别是对于只读请求来说几乎不存在同步开销问题[^2]。 ```java // JDK 1.7 示例代码片段 public V get(Object key) { int hash = hash(key); Segment<K,V> s = segmentFor(hash); // 获取对应的segment对象 return s.get(key, hash); // 调用segment内部方法获取value } // JDK 1.8 示例代码片段 final V doGet(Object key, int hash) { Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek; if ((tab = table) != null && (n = tab.length) > 0 && (e = tab[(n - 1) & hash]) != null) { ... } } ``` #### 性能改进 随着新架构的应用,JDK 1.8 下 `ConcurrentHashMap` 表现出更好的扩展性更低延迟特性。特别是在多核处理器环境中运行大型应用程序时优势明显,测试结果显示其吞吐量远高于旧版实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值