concurrentHashMap代码走读 chm走读

人人都是java并发专家_个人渣记录仅为自己搜索用的博客-CSDN博客

这三篇就够了

ConcurrentHashMap的初步使用场景、源码分析讲解(中) - 47号Gamer丶 - 博客园

面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(二) - 掘金

ConcurrentHashMap底层详解(图解扩容)(JDK1.8)_编程芝士的博客-CSDN博客_concurrenthashmap扩容

问 concurrentHashMap 如何确保并发迁移?

   数据迁移不停服设计借鉴concurrentHashMap chm _fei33423

ForwardingNode节点

1、标记作用,表示其他线程正在扩容,并且此节点已经扩容完毕
2、关联了nextTable,扩容期间可以通过find方法,访问已经迁移到了nextTable中的数据

nextTable

  • 扩容期间,将table数组中的元素 迁移到 nextTable。即nextTable为创建的新的table,容量为原来的两倍

问 chm和redis rehash的差别?

   redis 单插,多写,多删,多读. 优点是 写,删比较快. 比较适合redis. CHM我也觉也可以借鉴.

  Redis的字典渐进式扩容与ConcurrentHashMap的扩容策略比较 - 百度文库

问 chm 何时会resize?

0.75 等效与 n - n>>2 

 

 首先 s >= (long)(sc = sizeCtl) 此时 sizeCtl=0.75*n . 

ConcurrentHashMap底层详解(图解扩容)(JDK1.8)_编程芝士的博客-CSDN博客_concurrenthashmap扩容
 

sizeCtl

  • 多线程之间,以volatile的方式读取sizeCtl属性,来判断ConcurrentHashMap当前所处的状态。
  • 通过cas设置sizeCtl属性,告知其他线程ConcurrentHashMap的状态变更。

不同状态,sizeCtl所代表的含义也有所不同

  • 未初始化:sizeCtl=0:表示没有指定初始容量。
  • sizeCtl>0:表示初始容量。
  • 初始化中:sizeCtl=-1,标记作用,告知其他线程,正在初始化
  • 正常状态:sizeCtl=0.75n,扩容阈值
  • 扩容中:sizeCtl < 0 : 表示有其他线程正在执行扩容
  • sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT)+2 表示此时只有一个线程在执行扩容

问 chm 扩容大小如何变化?

  nextTab 是原来tab的两倍. transfer 内. 

  

问 红黑树什么时候转变?

   8个的时候,0.75 随机分布,概率很低很低. 

问 ConcurrentHashMap的源码分析- resizeStamp作用?

ConcurrentHashMap的源码分析-resizeStamp_Leon_Jinhai_Sun的博客-CSDN博客

所以resizeStamp(n)的返回值为:高16位置0,第16位为1,低15位存放当前容量n,用于表示是对n的扩容。

1. sizeCtl < 0
已经有线程在扩容,将sizeCtl+1并调用transfer()让当前线程参与扩容。
2. sizeCtl >= 0
表示没有线程在扩容,使用CAS将sizeCtl的值改为 ( resizeStamp << RESIZE_STAMP_SHIFT) + 2)。rs即resizeStamp(n)

问 chm 会有多个线程同时resize么?

   因为chm只原表的锁节点,故不同的线程可以同时对不同的分区进行迁移. 只要确保新的table对不同的线程都可见即可.

问 什么时候线程不协助扩容:

如果准备加入扩容的线程,发现以下情况,放弃扩容,直接返回。

a、发现transferIndex=0,即所有node均已分配
b、发现扩容线程已经达到最大扩容线程数

transferIndex 扩容索引

扩容索引,表示已经分配给扩容线程的table数组索引位置。主要用来协调多个线程,并发安全地获取迁移任务(hash桶)。

private transient volatile int transferIndex;
private static final int MIN_TRANSFER_STRIDE = 16; //扩容线程每次最少要迁移16个hash桶

1、在扩容之前,transferIndex 在数组的最右边 。此时有一个线程发现已经到达扩容阈值,准备开始扩容。
2、扩容线程,在迁移数据之前,首先要将transferIndex右移(以CAS的方式修改 transferIndex=transferIndex-stride(要迁移hash桶的个数)),获取迁移任务。每个扩容线程都会通过for循环+CAS的方式设置transferIndex,因此可以确保多线程扩容的并发安全。

换个角度,我们可以将待迁移的table数组,看成一个任务队列,transferIndex看成任务队列的头指针。而扩容线程,就是这个队列的消费者。扩容线程通过CAS设置transferIndex索引的过程,就是消费者从任务队列中获取任务的过程。为了性能考虑,我们当然不会每次只获取一个任务(hash桶),因此ConcurrentHashMap规定,每次至少要获取16个迁移任务(迁移16个hash桶,MIN_TRANSFER_STRIDE = 16)

也就是说transferIndex其实代表当前剩下需要进行扩容的桶的个数。如果transferIndex == 0 那就代表当前没有桶需要进行扩容了。每次一个线程参加扩容transferIndex就需要减MIN_TRANSFER_STRIDE。

问 如何实现同时都要求迁移?

问 tableSizeFor方法? 

通过输出可以大致猜到tableSizeFor的作用是返回一个大于输入参数且最小的为2的n次幂的数。dxxs

即 左零的个数 算出来比较关键. 或者说右移多少位后=0

问 为什么不允许为null? 

  二义性问题 ,对于 ConcurrentHashMap 不允许插入 null 值的问题,有人问过 ConcurrentHashMap 的作者 Doug Lea即getValue=null了,你不知道是key没有,还是真的没有value. 并发需要加锁 containsKey .

问无锁队列?

LMAX Disruptor

问SynchronizedQueue 无锁实现?

增加了线程池3倍性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值