JDK1.8-ConcurrentHashMap的 rehash 扩容逻辑

前言

此乃随笔, 用于记录ConcurrentHashMap的扩容逻辑, 这位大神 JDK1.8–深度分析CONCURRENTHASHMAP原理分析 的文章给了我莫大的鼓励。

顺带, 记录了HashMap的扩容细则。

至今为止, 我依旧没懂 ConcurrentHashMap 的扩容细则。

ConcurrentHashMap resize/rehash

1. 什么时候resize

跟HashMap一样, 使用Node数组, 数组有大小Capacity;
数组元素上挂Node结点, 可以是单链表(不过8位), 可以是红黑树。
.
因为是并发HashMap, 没有size字段(即时统计)。
加载因子loadFactor, 1.8已经不再使用。

  1. 每个Node结点的长度> 8时;
    1. 如果数组大小< 64, 优先扩容resize(rehash)
    2. 大于64,优先转红黑树。转时锁逻辑与put锁逻辑一致。
  2. ConcurrentHashMap元素个数(size) > sizeCtl(0.75 * tab.length)
    1. 每次resize(rehash)我完毕之后会一直检查是否需要再次resize(rehash)
    2. 源码判断逻辑:s >= (long)(sc = sizeCtl) (其中s=ConcurrentHashMap.size()
    3. 源码判断逻辑:sizeCtl = (n << 1) - (n >>> 1); (其中n=resize前的size)。
      1. 0.75: 2n - 0.5n = 1.5n
      2. resize后的大小2n
      3. 1.5n / 2n = 0.75

2. 怎么resize

这块没看透。看透再补充
.
ConcurrentHashMap 始终维护一个 Node[] table表示当前的数据, 以及一个 Node[] newTab表示将要resize去的表。

  1. 多线程resize(rehash)
    1. 每个线程负责16个Node, 最多允许 数组有大小Capacity / 16个线程。
    2. 正在执行put的那个线程,也可能参与resize(rehash)
    3. 参与resize(rehash), 仅在当前key对应的Node结点已经被转移,否则加锁继续put。
  2. resize是把旧Node转移到一个新的Node数组(源码2410行);
    1. resieze结束的标记(逻辑非常复杂,没懂,参阅 JDK1.8–深度分析CONCURRENTHASHMAP原理分析sizeCtl扩容退出机制章节), 或者直接看源码2414行。
    2. 每一个被转移完毕的Node结点的hash = -1(ForwardingNode);
  3. rehash的方式和 HashMap 的实现方式一致
    1. 如果是链表, 把链表的每个结点rehash到新的table中。
    2. 如果是红黑树, 也是一样的执行逻辑。

关于加锁退出机制, 以及Node结点长度大于8时, 以及创建table时, 逻辑相似。 都是resize(rehash)的过程。

HashMap resize/rehash

这个相对来说, 单线程, 就比较好理解了、当size > table.length * 0.75resize/rehash 执行。

  1. resize 直接将 旧 table 赋值为一个 2倍大小的新table
    1. 因此resize的时候可能导致get得到null(实际有值)。(多线程环境)
    2. 源码:Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];&&table = newTab;
  2. 迭代每个数组元素Node, 对链表和红黑树迁移。
  3. 单节点直接迁移(next = null)
    1. 数组位置算法: (table.length - 1) & hash
    2. table.length 为2^n, 则(table.length - 1)全都是二进制的1
    3. 扩容前后的值都是hash的值, 之前为null, 之后也还是为null. 可以直接赋值。
  4. 链表和红黑树分别rehash到新的table上去。

Redis resize/rehash

据说是 渐进式 rehash,https://www.cnblogs.com/meituantech/p/9376472.html 看不懂。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章