谈谈ConcurrentHashMap的扩容机制

1.7版本

  1. 1.7版本的ConcurrentHashMap是基于Segment分段实现的

  2. 每个Segment相对于⼀个⼩型的HashMap

  3. 每个Segment内部会进⾏扩容,和HashMap的扩容逻辑类似

  4. 先⽣成新的数组,然后转移元素到新数组中

  5. 扩容的判断也是每个Segment内部单独判断的,判断是否超过阈值

1.8版本

  1. 1.8版本的ConcurrentHashMap不再基于Segment实现

  2. 当某个线程进⾏put时,如果发现ConcurrentHashMap正在进⾏扩容那么该线程⼀起进⾏扩容

  3. 如果某个线程put时,发现没有正在进⾏扩容,则将key-value添加到ConcurrentHashMap中,然后判断是否超过阈值,超过了则进⾏扩容

  4. ConcurrentHashMap是⽀持多个线程同时扩容

  5. 扩容之前也先生成⼀个新的数组

  6. 在转移元素时,先将原数组分组,将每组分给不同的线程来进⾏元素的转移,每个线程负责⼀组或多组的元素转移⼯作

ConcurrentHashMap 的扩容机制

在 Java 中,ConcurrentHashMap 是一个线程安全的哈希表,它的扩容机制与 HashMap 有一些相似之处,但由于它支持并发操作,扩容时需要额外的同步控制,以保证线程安全。


1. ConcurrentHashMap 的数据结构

在 JDK 1.8 及以后,ConcurrentHashMap 采用 数组 + 链表 + 红黑树 的结构:

  • 数组 (Node<K,V>[] table):存储键值对的主要结构。
  • 链表:当哈希冲突发生时,链表存储多个键值对。
  • 红黑树:当链表长度超过 8 时,链表会转换为红黑树,以提高查询效率。

2. 触发扩容的条件

ConcurrentHashMap实际存储元素数量 (size()) 超过扩容阈值 (threshold) 时,触发扩容:

  • 扩容阈值 = 数组长度 × 负载因子 (默认负载因子 0.75)。
  • 默认初始大小16,即 threshold = 16 × 0.75 = 12
  • size() 超过 12 时,触发扩容,数组容量翻倍。

3. 扩容的具体流程

扩容的核心是 创建一个新的数组(容量是原来的 2 倍),并将旧数据迁移到新数组。流程如下:

(1) 计算新容量

  • 扩容后的 新数组容量 = 旧数组容量 × 2
  • 计算新的扩容阈值 newThreshold = newCapacity × loadFactor

(2) CAS 竞争控制扩容

  • ConcurrentHashMap 采用 分段迁移(分批扩容),避免所有线程都在扩容时造成阻塞。
  • 使用 sizeCtl 控制扩容状态
    • sizeCtl < 0 表示有线程正在扩容,其他线程会协助迁移数据。
    • sizeCtl > 0 表示扩容门槛,控制扩容的触发。

(3) 迁移数据(Rehashing)

数据迁移采用 多线程协作迁移

  • ConcurrentHashMap 通过 transfer() 方法 进行数据迁移。
  • 每个线程会尝试搬移部分数据(桶的迁移是按批次完成的)。
  • 迁移过程中,如果旧桶内元素是:
    • 单个节点(链表长度 ≤ 1):直接移动到新数组中的位置。
    • 链表节点(链表长度 > 1):会重新计算哈希值,可能拆分成两个新的链表。
    • 红黑树节点:如果还是满足红黑树条件,则保留红黑树结构,否则降级回链表。

(4) 迁移完成,更新引用

  • 迁移完成后,旧表引用会被丢弃,table 指向新的扩容后的数组。
  • sizeCtl 重新设置为新的扩容阈值,表示扩容完成。

4. 扩容的并发优化

(1) 分批迁移

不像 HashMap 需要一次性迁移所有数据,ConcurrentHashMap 采用 分段式扩容

  • 迁移时,每个线程处理 一个桶(bucket),加速迁移过程。
  • 其他线程可以并发参与数据迁移,提高效率,避免单线程阻塞。

(2) 低冲突的哈希算法

  • ConcurrentHashMap 采用 MurmurHash 变种 计算索引,减少哈希冲突,使数据更均匀地分布在不同桶中,降低扩容的负担。

5. 总结

扩容的特点

  1. 触发条件:当 size() 超过 threshold = capacity × 0.75 时触发扩容。
  2. 新容量计算:新容量 = 旧容量 × 2,迁移数据到新的桶。
  3. 并发优化
    • 分批迁移:多个线程可以并发搬移数据,加速迁移过程。
    • CAS 竞争控制:防止多个线程同时初始化扩容。
  4. 数据迁移方式
    • 单个节点直接复制
    • 链表可能拆分成两个部分
    • 红黑树保持结构或降级为链表

对比 HashMap

特性HashMapConcurrentHashMap
触发扩容size > thresholdsize > threshold
容量增长capacity × 2capacity × 2
扩容方式单线程 一次性迁移所有数据多线程分批迁移,避免阻塞
并发安全非线程安全线程安全,支持高并发

6. 相关源码

关键代码解析

(1) transfer() 方法
private final void transfer(Node<K,V>[] oldTab, Node<K,V>[] newTab) {
    int newCap = newTab.length;
    for (int i = 0; i < oldTab.length; i++) {
        Node<K,V> e;
        if ((e = oldTab[i]) != null) {
            oldTab[i] = null; // 清空旧数组的桶
            if (e.next == null) {
                newTab[e.hash & (newCap - 1)] = e; // 直接迁移
            } else if (e instanceof TreeNode) {
                // 红黑树迁移
                ((TreeNode<K,V>)e).split(newTab, i, oldCap);
            } else {
                // 处理链表拆分
                Node<K,V> loHead = null, loTail = null;
                Node<K,V> hiHead = null, hiTail = null;
                Node<K,V> next;
                do {
                    next = e.next;
                    if ((e.hash & oldCap) == 0) {
                        if (loTail == null) loHead = e;
                        else loTail.next = e;
                        loTail = e;
                    } else {
                        if (hiTail == null) hiHead = e;
                        else hiTail.next = e;
                        hiTail = e;
                    }
                } while ((e = next) != null);

                if (loTail != null) {
                    loTail.next = null;
                    newTab[i] = loHead; // 低位链表
                }
                if (hiTail != null) {
                    hiTail.next = null;
                    newTab[i + oldCap] = hiHead; // 高位链表
                }
            }
        }
    }
}

关键点解析:

  1. 单节点直接迁移:如果桶中只有一个元素,直接迁移到新数组。
  2. 链表迁移
    • 使用 (e.hash & oldCap) == 0 进行拆分,数据要么留在原索引,要么移动到 oldCap + i 的位置。
    • 避免重新计算哈希值,提升性能
  3. 红黑树迁移:保持红黑树结构,提升查询性能。

7. 结论

  • ConcurrentHashMap 采用分批扩容,使多个线程可以并发进行数据迁移,避免单线程扩容的性能瓶颈。
  • 迁移时,链表可能拆分成两个部分红黑树保持结构或降级为链表
  • 扩容的 主要优化点分批迁移 + CAS 竞争控制,适合高并发场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EthanMilk

你的鼓励是我创作的最大动力谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值