ConcurrentHashMap之9连问

1.聊聊ConcurrentHashMap 、源码,实现原理

  • 在Java 1.8 中数据结构是 数组+链表+红黑树。
    • 每个数据单元都是一个Node结构,结构中有Key ,value next ,hash 这几个属性。
    • next 字段是在Hash冲突后形成链表需要的。

2.ConcurrentHashMap并发能力为什么好于Hashtable

① Hashtable是通过对hash表整体进行锁定,使用synchronized来保证线程安全,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁

ConcurrentHashMap实现如下:
② jdk1.6的实现:ConcurrentHashMap是采用Segment分段锁的方式 其中 Segment 继承于 ReentrantLock,它并没有对整个数据结构进行锁定,而是局部锁定。如果容量大小是16,那个他的并发度就是16,相当于效率提高了16倍。
③ jdk1.8的实现:采用CAS + Synchronized,synchronized在put的时候锁是节点的第一个元素,但其底层还是“数组+链表+红黑树”的实现。当链表长度大于8并且数组长度大于64的时候转红黑树。

ps补充: CAS 获取数据时不加锁,通过volatile实现

2. sizeCtl 属性的含义

  • sizeCtl=0 默认状态,表示数组还没有被初始化。
  • sizeCtl =-1 表示正在初始化
    • 初始化散列表的时候使用,当多个线程同时执行到initTable逻辑的时候,使用CSA 修改 sizeCtl这个的值。
    • 期望值,初始值是0,更新成功后是-1 。
    • 并发下只有一个成功
    • CAS失败的线程会进行一个自旋检查,检查这个table是否被初始化出来了。
    • 每次自旋检查后会有短暂的释放所占用CPU
  • sizeCtl >0 表示下次触发扩容的阈值。
    • 比如等于12,则put后检查这个容量,当发现> =12就会触发扩容操作
  • sizeCtl <0 && sizeCtl !=-1 代表什么情况
    • 散列表正在扩容状态,高16位表示扩容表示戳,低16位表示参与扩容的工作线程数量 +1

3.如何保证线程安全

  • 若位置上是空的,添加新Node结点 ,通过Cas保证,
    • 成功就返回,若失败则表示被其它线程竞争到了
    • 则当前线程需要重新执行逻辑,再次尝试处理。
  • 若是已经存在Node节点非空,使用synchronized锁头结点

4. hash 寻址算法

  • 首先拿到 key 的 hashcode 然后再进行一个扰动运算。
  • 让hashCode 的高16位,与低16位进行一个异或运算。
  • 将符号位强制设置为0,让整个HASH值成为一个正数,
  • 最终是 >=0 <= (table.length-1)

5.ConcurrentHashMap为什么不采用 AtomicLong 统计数量

  • 性能问题。
  • CAS 比较替换,如果期望值与内存值是一致的再执行替换操作,并且CSA 反应到内核层面
  • 其实是cmpxchg 指令 ,当这个指令执行的时候会检测当前平台是否为多核平台
  • 如果是多核的话,cmpxchg 会通过锁总线,的形式来保证同一时刻,只能有一个cpu执行。
  • 反应到底层仍然是串行,高并发下,陪跑的线程太多,损耗CPU

6.LongAdder 如何解决了大并发下,性能问题?

分段锁的概念使用CAS锁数组的一个格

7.size()方法是怎么实现的?

  • 最核心的是 volatile 修饰 Long类型的 baseCount 字段,+ CounterCell 数组,
    • 数组内是一个 volatile long value
  • 若从来没有产生并发失败(CAS修改baseCount 失败)
    • 数据会全部累计到baseCount字段 ,采用Cas更新baseCount字段
    • 当某个线程冲突,cas 修改baseCount字段冲突的时候,就将cell [] 数组数据架构构建
    • 之后的累计请求,就不在首选baseCount字段了,而是根据分配给线程的hash值,进行位于运算,找到对应的cell[]
    • 将累加的值,通过cas方式写入到cell里。

8.longAdder 的内部结构

  • 最核心的是Long类型的Base字段,+ cell 数组
  • Cell 结构里面有一个volatile Long类型的Value 字段
  • 使用longAdder的过程中,若从来没有产生并发失败(CAS修改base 失败)
    • 数据会全部累计到base字段 ,采用Cas更新Base字段
    • 当某个线程冲突,cas 修改base字段冲突的时候,就将cell [] 数组数据架构构建
    • 之后的累计请求,就不在首选base字段了,而是根据分配给线程的hash值,进行位于运算,找到对应的cell[]
    • 将累加的值,通过cas方式写入到cell里。

9.ConcurrentHashMap 进行 put 的步骤

  • 首先对 Key求Hash值。计算所在数组下标。
  • 判断数组table是否为空。为空则进行初始化。
  • 若没有Hash碰撞 ,则调用CAS插入相应的数据
  • 若存在碰撞,则对该节点加synchronized锁,锁的是节点的第一个元素,遍历链表插入新节点
  • 若链表长度>8(put第9个的时候)则调用转红黑树的方法
    • 内部判断若数组<64 则进仅行扩容,若>= 64 则转成红黑树。
  • 若匹配到旧key ,值不为空,则返回旧值,本次操作是更新操作。
上一篇:Cell | 蛋白基因组学揭示胰腺癌潜在治疗靶点与早期诊断标志


下一篇:html功能