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

ConcurrentHashMap抛出IllegalStateException: Recursive Update问题排查

问题分析与解决方案

核心结论

这是Java 11版本中ConcurrentHashMap.computeIfAbsent方法的并发场景误判问题,并非你的使用疏漏。

异常原因解析

你对源码的分析完全正确:

  • Java 11的ConcurrentHashMap在computeIfAbsent逻辑中,当映射函数(generateImplementation)执行耗时较长时,其他线程可能已经完成当前key的节点插入操作。
  • 当前线程执行到pred.next != null检查时,会误将这种合法的并发插入判定为递归更新,抛出IllegalStateException: Recursive update,但堆栈中并没有递归调用的痕迹。
  • Java 8的computeIfAbsent实现没有这个检查逻辑,因此你的代码在Java 8下正常,仅在Java 11出现问题。

同时可以确认你的使用完全合规:映射函数是无副作用的纯函数,没有违反“映射函数不得在计算期间修改此映射”的API要求。

可行解决方案

1. 手动实现双重检查锁定(Double-Checked Locking)

绕过computeIfAbsent的内部检查,手动控制并发缓存逻辑:

public Class<? extends _Artifact_> getImplementationOf(Class<_Artifact_> type, boolean defaultPackage) {
    // 第一次非同步检查
    Class<? extends _Artifact_> clazz = implementationClassCache.get(type);
    if (clazz == null) {
        // 针对缓存加锁,避免重复生成
        synchronized (implementationClassCache) {
            // 第二次同步检查
            clazz = implementationClassCache.get(type);
            if (clazz == null) {
                clazz = generateImplementation(type, defaultPackage);
                implementationClassCache.put(type, clazz);
            }
        }
    }
    return clazz;
}

注:如果缓存key数量极大,也可以针对单个key的哈希值加锁,减少锁竞争,但实现复杂度会更高。

2. 升级Java版本

该误判问题在后续Java版本(如Java 17)中已被修复,调整了computeIfAbsent的内部检查逻辑,避免将合法的并发插入判定为递归更新。

3. 替换为Guava LoadingCache

使用Guava的LoadingCache,它的并发加载逻辑经过充分优化,天然支持这种耗时的缓存生成场景:

// 初始化缓存
private final LoadingCache<Class<_Artifact_>, Class<? extends _Artifact_>> implementationClassCache =
        CacheBuilder.newBuilder()
                .concurrencyLevel(Runtime.getRuntime().availableProcessors())
                .build(new CacheLoader<>() {
                    @Override
                    public Class<? extends _Artifact_> load(Class<_Artifact_> type) throws Exception {
                        return generateImplementation(type, false); // 根据需求调整defaultPackage参数
                    }
                });

// 获取实现类方法
public Class<? extends _Artifact_> getImplementationOf(Class<_Artifact_> type, boolean defaultPackage) {
    // 若defaultPackage为可变参数,需将key改为包含该参数的组合类(如Pair)
    try {
        return implementationClassCache.get(type);
    } catch (ExecutionException e) {
        throw new RuntimeException(e.getCause());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 08:30:37