Z次元
文章 笔记 日志
专题
专栏分类
文章归档
友链
友情链接
朋友圈
留言
头像
系列文章
JDK8升级JDK21报错解决方案记录
系列文章
知识积累
更新于:2025/12/30
|
发布于: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
评论区

删除确认

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

这里还没有评论哦

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

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