You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ConcurrentHashMap的initTable方法为何要两次检查table是否为空?

ConcurrentHashMap initTable() 二次校验问题解答

相关源码回顾

private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

关于你的猜测的正确性

你的猜测不成立。ConcurrentHashMap的table成员变量是被volatile修饰的,根据Java内存模型的volatile内存语义:
对volatile变量的写操作,之前的所有普通写操作(包括新Node数组的创建、赋值操作)都不会被重排序到volatile写之后。代码中sizeCtl = sc的赋值操作,执行顺序在volatile写table = nt之后,因此不可能被JVM重排序到数组创建操作之前。

第二次校验的适用场景

这是典型的**双重检查锁定(DCL)**实现,核心作用是避免多线程并发竞争时的重复初始化问题,对应的经典并发时序如下:

  1. 初始状态:ConcurrentHashMap刚完成构造,table = null,sizeCtl = 16(假设指定初始容量为16)
  2. 线程T1执行initTable(),通过第一次while校验(tab = table) == null,正准备读取sizeCtl时CPU时间片耗尽,被系统挂起
  3. 线程T2进入initTable(),顺利通过第一次校验,读取sizeCtl = 16,CAS操作成功将sizeCtl改为-1抢到初始化权限
  4. T2完成数组初始化,将table赋值为长度16的Node数组,计算得到新的阈值sc = 12,finally块中将sizeCtl更新为12,初始化完成退出
  5. 此时T1被唤醒,继续执行读取到当前sizeCtl = 12,CAS操作成功将sizeCtl从12改为-1,抢到权限进入try块
  6. 如果没有第二次校验,T1会重新创建新的Node数组,覆盖已经初始化完成、甚至已经存入了数据的table,造成数据丢失。有了第二次校验,T1会读取到最新的table已经不为空,直接跳过初始化逻辑,不会触发重复创建。

两次校验的分工

  • 第一次校验:快速路径,绝大多数场景下数组已经初始化完成,不需要进入循环抢CAS,直接返回即可,提升调用性能
  • 第二次校验:安全兜底,确保只有第一个真正抢到初始化权限、且此时数组确实未初始化的线程,才会执行初始化操作,从根源避免重复初始化的线程安全问题。

内容的提问来源于stack exchange,提问作者peter Char

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 16:45:02