ConcurrentHashmap详解

简介

ConcurrentHashMap是hashMap的高性能并发版本

高性能的原因

ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,只要多个修改操作发生在不同的段上,它们就可以并发进行。理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。

Hash

散列,哈希:把任意长度的输入通过一种算法(散列),变换成为固定长度的输出,这个输出值就是散列值。属于压缩映射,容易产生哈希冲突。Hash算法有直接取余法等。
产生哈希冲突时解决办法:开放寻址;2、再散列;3、链地址法(相同hash值的元素用链表串起来)。
ConcurrentHashMap在发生hash冲突时采用了链地址法。

常用位运算(JDK源码中有很多下列位操作)

  • 位与 & (1&1=1 1&0=0 0&0=0)
  • 位或 | (1|1=1 1|0=1 0|0=0)
  • 位非 ~ ( ~1=0 ~0=1)
  • 位异或 ^ (1^1=0 1^0=1 0^0=0)
  • <<有符号左移 >>有符号的右移 >>>无符号右移 例如:8 << 2 = 32 8>>2 = 2
  • 取模的操作 a % (Math.pow(2,n)) 等价于 a&( Math.pow(2,n)-1)

初始化

初始化有三个参数
initialCapacity:初始容量大小 ,默认16。
loadFactor, 扩容因子,默认0.75,当一个Segment存储的元素数量大于initialCapacity* loadFactor时,该Segment会进行一次扩容。
concurrencyLevel 并发度,默认16。并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPU cache命中率会下降,从而引起程序性能下降。

初始化部分代码解析:

在这里插入图片描述
保证Segment数组的大小,一定为2的幂,例如用户设置并发度为17,则实际Segment数组大小则为32
在这里插入图片描述
保证每个Segment中tabel数组的大小,一定为2的幂,初始化的三个参数取默认值时,table数组大小为2
在这里插入图片描述
初始化Segment数组,并实际只填充Segment数组的第0个元素。
在这里插入图片描述
用于定位元素所在segment。segmentShift表示偏移位数,通过前面的int类型的位的描述我们可以得知,int类型的数字在变大的过程中,低位总是比高位先填满的,为保证元素在segment级别分布的尽量均匀,计算元素所在segment时,总是取hash值的高位进行计算。segmentMask作用就是为了利用位运算中取模的操作:l a % (Math.pow(2,n)) 等价于 a&( Math.pow(2,n)-1)

ConcurrentHashMap 分为 1.7 、1.8 版,两者在实现上略有不同。

Base 1.7

结构图

在这里插入图片描述
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment实际继承自可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,每个Segment里包含一个HashEntry数组,我们称之为table,每个HashEntry是一个链表结构的元素。

核心成员变量

/**
 * Segment 数组,存放数据时首先需要定位到具体的 Segment 中。
 */
final Segment<K,V>[] segments;
transient Set<K> keySet;
transient Set<Map.Entry<K,V>> entrySet;

Segment

Segment 是 ConcurrentHashMap 的一个内部类,注意Segment继承自ReentrantLock,主要的组成如下:

static final class Segment<K,V> extends ReentrantLock implements Serializable {
   
       private static final long serialVersionUID = 2249069246763182397L;

       // 和 HashMap 中的 HashEntry 作用一样,真正存放数据的桶
       transient volatile HashEntry<K,V>[] table;
       transient 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值