Map 综述—彻头彻尾理解 ConcurrentHashMap

本文详细介绍了ConcurrentHashMap的数据结构,特别是其利用Segment实现的锁分段技术,以及put操作的并发处理。重点关注了初始容量、负载因子和并发级别对性能的影响。
摘要由CSDN通过智能技术生成

final int segmentMask; // 用于定位段,大小等于segments数组的大小减 1,是不可变的

final int segmentShift; // 用于定位段,大小等于32(hash值的位数)减去对segments的大小取以2为底的对数值,是不可变的

final Segment<K,V>[] segments; // ConcurrentHashMap的底层结构是一个Segment数组

段的定义:Segment

Segment 类继承于 ReentrantLock 类,从而使得 Segment 对象能充当锁的角色

Map 综述—彻头彻尾理解 ConcurrentHashMap

在Segment类中,count 变量是一个计数器,它表示每个 Segment 对象管理的 table 数组包含的 HashEntry 对象的个数,也就是 Segment 中包含的 HashEntry 对象的总数。特别需要注意的是,之所以在每个 Segment 对象中包含一个计数器,而不是在 ConcurrentHashMap 中使用全局的计数器,是对 ConcurrentHashMap 并发性的考虑:因为这样当需要更新计数器时,不用锁定整个ConcurrentHashMap

基本元素:HashEntry

与HashMap中的Entry类似,HashEntry也包括同样的四个域,分别是key、hash、value和next。不同的是,在HashEntry类中,key,hash和next域都被声明为final的,value域被volatile所修饰,因此HashEntry对象几乎是不可变的,这是ConcurrentHashmap读操作并不需要加锁的一个重要原因

由于value域被volatile修饰,所以其可以确保被读线程读到最新的值,这是ConcurrentHashmap读操作并不需要加锁的另一个重要原因。

ConcurrentHashMap 的构造函数

======================================================================================

1,ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)

该构造函数意在构造一个具有指定容量、指定负载因子和指定段数目/并发级别(若不是2的幂次方,则会调整为2的幂次方)的空ConcurrentHashMap

2,ConcurrentHashMap(int initialCapacity, float loadFactor)

该构造函数意在构造一个具有指定容量、指定负载因子和默认并发级别(16)的空ConcurrentHashMap

3,ConcurrentHashMap(int initialCapacity)

该构造函数意在构造一个具有指定容量、默认负载因子(0.75)和默认并发级别(16)的空ConcurrentHashMap

4,ConcurrentHashMap()

该构造函数意在构造一个具有默认初始容量(16)、默认负载因子(0.75)和默认并发级别(16)的空ConcurrentHashMap

5,ConcurrentHashMap(Map<? extends K, ? extends V> m)

该构造函数意在构造一个与指定 Map 具有相同映射的 ConcurrentHashMap,其初始容量不小于 16 (具体依赖于指定Map的大小),负载因子是 0.75,并发级别是 16, 是 Java Collection Framework 规范推荐提供的

小结

在这里,我们提到了三个非常重要的参数:初始容量负载因子并发级别,这三个参数是影响ConcurrentHashMap性能的重要参数

ConcurrentHashMap 的数据结构

==========================================================================================

通过使用段(Segment)将ConcurrentHashMap划分为不同的部分,ConcurrentHashMap就可以使用不同的锁来控制对哈希表的不同部分的修改,从而允许多个修改操作并发进行, 这正是ConcurrentHashMap锁分段技术的核心内涵

ConcurrentHashMap 的并发存取

==========================================================================================

在ConcurrentHashMap中,线程对映射表做读操作时,一般情况下不需要加锁就可以完成,对容器做结构性修改的操作(比如,put操作、remove操作等)才需要加锁。

用分段锁机制实现多个线程间的并发写操作: put(key, vlaue)

在ConcurrentHashMap中,典型结构性修改操作包括put、remove和clear,下面我们首先以put操作为例说明对ConcurrentHashMap做结构性修改的过程

public V put(K key, V value) {

if (value == null)

throw new NullPointerException();

int hash = hash(key.hashCode());

return segmentFor(hash).put(key, hash, value, false);

}

ConcurrentHashMap不同于HashMap,它既不允许key值为null,也不允许value值为null

定位段的segmentFor()方法源码如下

final Segment<K,V> segmentFor(int hash) {

return segments[(hash >>> segmentShift) & segmentMask];

}

根据key的hash值的高n位就可以确定元素到底在哪一个Segment中

段的put()方法的源码如下所示

V put( K key, int hash, V value, boolean onlyIfAbsent )

{

lock(); /* 上锁 */

try {

int c = count;

if ( c++ > threshold ) /* ensure capacity */

rehash();

HashEntry<K, V>[] tab = table; /* table是Volatile的 */

int index = hash & (tab.length - 1); /* 定位到段中特定的桶 */

HashEntry<K, V> first = tab[index]; /* first指向桶中链表的表头 */

HashEntry<K, V> e = first;

/* 检查该桶中是否存在相同key的结点 */

while ( e != null && (e.hash != hash || !key.equals( e.key ) ) )

e = e.next;

V oldValue;

if ( e != null ) /* 该桶中存在相同key的结点 */

{

oldValue = e.value;

if ( !onlyIfAbsent )

e.value = value; /* 更新value值 */

}else { /* 该桶中不存在相同key的结点 */

oldValue = null;

++modCount; /* 结构性修改,modCount加1 */

tab[index] = new HashEntry<K, V>( key, hash, first, value ); /* 创建HashEntry并将其链到表头 */

count = c; /* write-volatile,count值的更新一定要放在最后一步(volatile变量) */

}

return(oldValue); /* 返回旧值(该桶中不存在相同key的结点,则返回null) */

} finally {

unlock(); /* 在finally子句中解锁 */
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

一线互联网大厂Java核心面试题库

image

正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值