并发容器,深入理解CurrentHashMap分段锁,源代码剖析

CurrentHashMap

20201228

15:03

目录

-

- 1. 引言

- 2. 分段锁数据结构

- 3. JDK1.7实现

    - 3.1 初始化

    - 3.2 插入

- 4. JDK1.8实现

    - 4.1 初始化

    - 4.2 插入

- 5. REF

 

 

1. 引言

CurrentHashMap(CHM)是并发容器的代表,是一种实现细粒度锁提高并发性能的容器。

它的核心思想是分段锁,即缩小每次锁的数据的数量,从而提高并发性能。

话又说回来,我回想锁一大堆机制,其实本质上应该不是一件复杂的事。类比一下,这件事类似于几人搬箱子(假设一个人只能搬一个),提高效率的方式:

  • 要么你把箱子拆成多份(细粒度的锁)
  • 要么一个人搬,其他人就在旁边等等,不要休息(因为休息阻塞会花费大量的时间,乐观锁就是基于自旋等待,假设自旋很快)

CurrentHashMap在jdk1.7和1.8之间发生了较大的变化,但事实上和HashMap的实现很像。JDK1.7引入了segment的二阶段数据结构,每次只对segment加锁,JDK1.8则和hashMap非常像,只是它加锁只加hash值指向的节点。

2. 分段锁数据结构

这是CHM的灵魂,不过下图其实是JDK1.7的实现,而JDK1.8并没有这种显示的两阶段数据结构,但是思想是一样的。

 

3. JDK1.7实现

3.1 初始化

  • 1.7 是创建立马初始化,1.8是put的时候进行初始化
  • 初始化主要是初始segment表和一些计算hash用的标志,掩码等

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

    2     // 参数校验

    3     if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)

    4         throw new IllegalArgumentException();

    5     // 校验并发级别大小,大于 1<<16,重置为 65536

    6     if (concurrencyLevel > MAX_SEGMENTS)

    7         concurrencyLevel = MAX_SEGMENTS;

    8     // Find power-of-two sizes best matching arguments

    9     // 2的多少次方

   10     int sshift = 0;

   11     int ssize = 1;

   12     // 这个循环可以找到 concurrencyLevel 之上最近的 2的次方值

   13     while (ssize < concurrencyLevel) {

   14         ++sshift;

   15         ssize <<= 1;

   16     }

   17     // 记录段偏移量,计算hash,mod用

   18     this.segmentShift = 32 - sshift;

   19     // 记录段掩码

   20     this.segmentMask = ssize - 1;

   21     // 设置容量

   22     if (initialCapacity > MAXIMUM_CAPACITY)

   23         initialCapacity = MAXIMUM_CAPACITY;

   24     // c = 容量 / ssize ,默认 16 / 16 = 1,这里是计算每个 Segment 中的类似于 HashMap 的容量

   25     int c = initialCapacity / ssize;

   26     if (c * ssize < initialCapacity)

   27         ++c;

   28     int cap = MIN_SEGMENT_TABLE_CAPACITY;

   29     //Segment 中的类似于 HashMap 的容量至少是2或者2的倍数

   30     while (cap < c)

   31         cap <<= 1;

   32     // create segments and segments[0]

   33     // 创建 Segment 数组,设置 segments[0]

   34     Segment<K,V> s0 = new Segment<K,V>(loadFactor, (int)(cap * loadFactor),

   35                          (HashEntry<K,V>[])new HashEntry[cap]);

   36     Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];

   37     UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]

   38     this.segments = ss;

   39 }

3.2 插入

对于整个map的插入 由于是分段的数据结构,逻辑就是

  • 获得分段位置
  • 往分段位置的hash表中插入

    1 public V put(K key, V value) {

    2     Segment<K,V>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值