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块的作用就是兜底恢复这个控制变量的状态:
- 正常流程:初始化完成后,代码会把
sc计算为数组容量的75%(也就是默认的负载阈值),finally块会把这个值赋值给sizeCtl,让后续的put、get等操作能正常识别数组的负载状态。 - 异常流程:如果初始化过程中抛出异常(比如创建数组时内存不足抛出
OutOfMemoryError),如果没有finally块,sizeCtl会一直保持-1的锁定状态。其他线程会永远自旋等待,无法再尝试初始化,整个ConcurrentHashMap就彻底卡死了。
有了finally块之后,无论try里的代码是否抛出异常,都会把sizeCtl恢复为合理的值(要么是计算好的负载阈值,要么是最初的sizeCtl初始值),这样其他线程就能继续尝试抢占初始化权限,保证Map还有机会正常工作。
简单来说,这个try块是并发场景下的容错保障,防止一次初始化失败就导致整个Map彻底不可用。
内容的提问来源于stack exchange,提问作者JGFMK
相关产品推荐
相关产品推荐

