Z次元
文章 笔记 日志
专题
专栏分类
文章归档
友链
友情链接
朋友圈
留言
头像
系列文章
JDK8升级JDK21报错解决方案记录
系列文章
知识积累
更新于:2026/1/21
|
发布于:2025/12/28
文章摘要
......

前言

前段时间,公司项目升级了JDK21,由于SpringBoot2的一些致命漏洞,其实领导很早就下达了任务,年底是最后的期限。至于升级过程中大部分都依靠AI Agent完成的,再加上我们项目组是最晚升级的那一批,所以升级过程还算比较顺利,但还是遇到了一些坑,这里记录下我在升级过程中踩到的坑。

CompletableFuture异步任务报错

报错

在使用CompletableFuture.runAsync() 方法异步调用其它微服务时,出现了报错:

org.springframework.http.client.ClientHttpRequest referenced from a method is not visible from class loader: 'app'

原因

核心原因:

当直接使用 CompletableFuture.runAsync(() -> { ... }) 而不指定线程池时,默认使用的是 JDK 的 ForkJoinPool.commonPool()。

问题在于:

ForkJoinPool 中的线程通常是由 System ClassLoader(系统类加载器)加载的,但在 JDK 9 引入模块化系统(JPMS)之后,一直到 JDK 21,JVM 对 ForkJoinPool 做了严格的限制。

当异步线程试图加载 ClientHttpRequest 等 Spring 类时,由于类加载器隔离(Visibility),ForkJoinPool 的线程看不到 LaunchedURLClassLoader 中的类(位于 BOOT-INF/lib 下),从而报出 not visible from class loader 错误。

解决方案

网上大多数解决方案基本有两种,1. 自定义 ForkJoinPool 工厂 2. 自定义线程池

其中自定义 ForkJoinPool 工厂有点麻烦,博主并不推荐,自定义线程池倒是一个比较稳的方案。不过除了这两种方案,经过博主实践,其实还有两种更简单的方案,那就是直接使用虚拟线程池或者使用@Async注解替换CompletableFuture

自定义线程池

@EnableAsync
public class AsyncConfig {

    @Bean("ahzooAsyncExecutor")
    public Executor ahzooAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("Async-Ahzoo-Service-");
        // 这一步非常关键,它会初始化线程并继承父线程的上下文
        executor.initialize();
        return executor;
    }
}
@Autowired
@Qualifier("ahzooAsyncExecutor")
private Executor executor;

public void callMicroservice() {
    CompletableFuture.runAsync(() -> {
       // xxxxxxx
    }, executor); // <--- 关键点:传入 executor
}

虚拟线程

直接在配置文件中全局开启虚拟线程:

spring:
  threads:
    virtual:
      enabled: true

自定义ForkJoinPool 工厂

关于自定义自定义 ForkJoinPool 工厂的方法,可以在stackoverflow的这篇文章找到相关回答:CompletableFuture / ForkJoinPool Set Class Loader

版权声明
本文依据 CC-BY-NC-SA 4.0 许可协议授权,请您在转载时注明文章来源为 Z次元 ,若本文涉及转载第三方内容,请您一同注明。
更多专栏文章推荐
知识积累
人非生而知之者,孰能无惑?惑而不从师,其为惑也,终不解矣。
MySQL模糊查询的多种方式
2025/12/4
JDK新版特性(22-25)
2025/11/9
评论区

删除确认

评论删除后无法恢复,请确认是否继续?
发表评论

这里还没有评论哦

快来发一条评论抢占前排吧

目录
1
前言
2
CompletableFuture异步任务报错
报错
原因
解决方案
自定义线程池
虚拟线程
自定义ForkJoinPool 工厂
目录
1
前言
2
CompletableFuture异步任务报错
报错
原因
解决方案
自定义线程池
虚拟线程
自定义ForkJoinPool 工厂
博客
文章 笔记 日志
专题
专栏分类 文章归档
友链
友情链接 朋友圈
交流
留言 关于我
主页
菜单
置顶
主题
我的
十玖八柒
每天进步多一点
欢迎到访φ(゜▽゜*)♪
最新评论
柒: @Honesty
因为这个系列主要是总结和代码相关的特性,不过这个特性感觉确实可以写一下
Honesty:
哎?Jdk25 对象头压缩这个很重要的特性没记呢
个人占星:
DeepSeek没有想象中的好用
个人占星:
想给自己的网站弄个统计功能,但不会弄,头疼
我的
关于我
个人主页
站点地图
RSS订阅
导航
十年之约
虫洞穿梭
开源博客
前端开源仓库
后端开源仓库
©2020 - 2026 By 十玖八柒 版权所有
豫ICP备20021466号