Zulu OpenJDK21虚拟线程运行数天触发InterruptedException致应用重启
虚拟线程join时被中断导致应用崩溃的排查与解决
问题背景
应用基于Zulu OpenJDK 21 + Spring Boot构建,为高效管理线程封装了等待多线程完成的工具方法joinThreads,运行约5天后出现InterruptedException,导致应用崩溃重启;重启后可正常运行一段时间。
核心代码
public static void joinThreads(Thread... threadList) { try { for (Thread thread : threadList) { if (thread == null) { continue; } thread.join(); } } catch (InterruptedException e) { log.error("Virtual thread task exception", e); throw new BizException(CodeEnum.INTERNAL_BUSINESS_FAIL, "Virtual thread task was interrupted"); } }
异常栈信息
ERROR com.xmonecode.tccompass.common.utils.VirtualThreadUtil - Virtual thread task exception java.lang.InterruptedException: null at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1100) at java.base/java.util.concurrent.CountDownLatch.await(CountDownLatch.java:230) at java.base/java.lang.VirtualThread.joinNanos(VirtualThread.java:824) at java.base/java.lang.Thread.join(Thread.java:2061) at java.base/java.lang.Thread.join(Thread.java:2154) at com.xmonecode.tccompass.common.utils.VirtualThreadUtil.joinThreads(VirtualThreadUtil.java:40) at com.xmonecode.tccompass.common.utils.VirtualThreadUtil.joinStartVirtualThread(VirtualThreadUtil.java:70) at com.xmonecode.tccompass.interceptor.RebateInterceptor.preHandle(RebateInterceptor.java:45) at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:146) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
额外环境信息
- JDK版本:zulu-openjdk:21
- 框架:Spring Boot
- 异常触发位置:
CountDownLatch.await()和VirtualThread.joinNanos()调用过程中 - 运行环境:容器化部署,崩溃后自动重启
排查与解决建议
- 检查容器层面的中断信号:容器环境下,资源超限(如OOM触发内核杀死进程)、健康探测失败(K8s liveness/readiness探针)、运维操作等都可能向进程发送中断信号。查看容器日志、宿主机
dmesg日志(OOM记录)、K8s事件等,确认是否有外部触发的中断。 - 排查HTTP请求超时:异常发生在Spring MVC拦截器的
preHandle阶段,若请求超时(如Tomcat的connectionTimeout、Spring Boot的server.servlet.session.timeout或接口级别的超时配置),容器会中断请求处理线程,进而导致关联的虚拟线程join操作被中断。检查相关超时配置是否合理。 - 定位代码内部的中断源:排查应用中是否有其他代码调用
Thread.interrupt()方法,尤其是针对虚拟线程或其所在线程组。可在joinThreads方法中增加日志,记录当前线程、待join线程的信息,以及中断发生时的线程栈,帮助定位。 - 检查JDK版本问题:确认Zulu OpenJDK 21是否存在虚拟线程join相关的已知bug,尝试升级到最新的Zulu 21小版本,修复可能的底层问题。
- 优化异常处理逻辑:当前捕获中断后直接抛出异常导致应用崩溃,可改为恢复中断状态、记录详细日志后优雅降级,避免应用直接崩溃。示例修改:
public static void joinThreads(Thread... threadList) { try { for (Thread thread : threadList) { if (thread == null) { continue; } thread.join(); } } catch (InterruptedException e) { // 恢复中断状态,让上层处理 Thread.currentThread().interrupt(); log.error("线程join被中断,当前线程: {}, 待join线程: {}", Thread.currentThread().getName(), Arrays.stream(threadList).filter(Objects::nonNull).map(Thread::getName).toList(), e); // 根据业务场景选择降级逻辑,而非直接抛出致命异常 // throw new BizException(CodeEnum.INTERNAL_BUSINESS_FAIL, "Virtual thread task was interrupted"); } }
- 增加线程监控:通过Micrometer等工具监控虚拟线程的数量、状态,记录中断发生时的上下文信息,便于后续分析触发场景。
内容的提问来源于stack exchange,提问作者Loveshy
相关产品推荐
相关产品推荐

