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

Java ConcurrentHashMap initTable()方法中try/finally块的作用解析

为什么ConcurrentHashMap的initTable方法需要使用try块?

这个问题问得很到位!咱们来拆解下这段代码里try块的核心作用。

先把代码贴出来方便分析:

/**
 * 初始化表,使用sizeCtl中记录的大小。
 */
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(); // 初始化竞争失败;仅自旋
        else if (U.compareAndSetInt(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初始化内部数组的核心逻辑,其中sizeCtl是一个关键的控制变量:

  • 当某个线程通过CAS把sizeCtl设为-1时,相当于抢占了初始化独占权限,其他线程看到sizeCtl < 0就会进入Thread.yield()自旋等待,不会干扰初始化操作。

而try-finally块的作用就是兜底恢复这个控制变量的状态:

  1. 正常流程:初始化完成后,代码会把sc计算为数组容量的75%(也就是默认的负载阈值),finally块会把这个值赋值给sizeCtl,让后续的put、get等操作能正常识别数组的负载状态。
  2. 异常流程:如果初始化过程中抛出异常(比如创建数组时内存不足抛出OutOfMemoryError),如果没有finally块,sizeCtl会一直保持-1的锁定状态。其他线程会永远自旋等待,无法再尝试初始化,整个ConcurrentHashMap就彻底卡死了。

有了finally块之后,无论try里的代码是否抛出异常,都会把sizeCtl恢复为合理的值(要么是计算好的负载阈值,要么是最初的sizeCtl初始值),这样其他线程就能继续尝试抢占初始化权限,保证Map还有机会正常工作。

简单来说,这个try块是并发场景下的容错保障,防止一次初始化失败就导致整个Map彻底不可用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:37:53