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

使用mvn exec:java执行并行流代码出现线程残留警告求助

问题分析与解决建议

你的代码

IntStream.range(0, N)
        .parallel()
        .forEach(
            i -> {
// do some work
            });    

出现的警告

[WARNING] thread Thread[#73,ForkJoinPool.commonPool-worker-8,5,] was interrupted but is still alive after waiting at least 15000msecs
[WARNING] thread Thread[#73,ForkJoinPool.commonPool-worker-8,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#74,ForkJoinPool.commonPool-worker-9,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#75,ForkJoinPool.commonPool-worker-10,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#76,ForkJoinPool.commonPool-worker-11,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#77,ForkJoinPool.commonPool-worker-12,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#78,ForkJoinPool.commonPool-worker-13,5,] will linger despite being asked to die via interruption
[WARNING] thread Thread[#79,ForkJoinPool.commonPool-worker-14,5,] will linger despite being asked to die via interruption
[WARNING] NOTE: 7 thread(s) did not finish despite being asked to via interruption. This is not a problem with exec:java, it is a problem with the running code. Although not serious, it should be remedied.

已知信息

  • lambda表达式中未抛出异常
  • mvn spring-boot:run(启动新JVM)无警告,mvn exec:java(复用Maven进程的JVM)出现警告

原因解析

问题核心是ForkJoinPool.commonPool()的工作线程默认是非守护线程:

  • 用exec:java时,代码在Maven的JVM进程中运行,main方法执行完毕后,Maven会尝试中断所有未终止的非守护线程以退出进程,但commonPool的线程会持续存活等待新任务,无法及时响应中断,从而触发警告。
  • 用spring-boot:run时,代码在独立JVM中运行,应用正常关闭时会正确处理线程生命周期,或JVM退出时会等待非守护线程自然终止,不会触发Maven的中断逻辑。

解决办法

1. 自定义ForkJoinPool并手动管理生命周期

放弃使用全局commonPool,自行创建线程池并在任务完成后关闭,确保线程全部终止:

// 根据业务需求调整线程数
try (ForkJoinPool customPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors())) {
    // 提交并行流任务并等待完成
    customPool.submit(() -> 
        IntStream.range(0, N)
                 .parallel()
                 .forEach(i -> {
                     // do some work
                 })
    ).join();
} // try-with-resources会自动关闭线程池

2. 将commonPool线程设置为守护线程

通过自定义线程工厂,把commonPool的线程改为守护线程,这样JVM退出时不会等待这些线程:

  • 先实现线程工厂类:
public class DaemonForkJoinThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
    @Override
    public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
        thread.setDaemon(true); // 标记为守护线程
        return thread;
    }
}
  • 启动时添加JVM参数(可在Maven的exec:java配置里指定):
-Djava.util.concurrent.ForkJoinPool.common.threadFactory=com.yourpackage.DaemonForkJoinThreadFactory

注意:该方法全局生效,会影响所有使用commonPool的代码,需谨慎使用。

3. 主线程结束前等待commonPool空闲

在main方法结束前,等待commonPool的所有任务完成且线程进入空闲状态:

// 等待10秒,超时后强制退出(可根据实际情况调整超时时间)
ForkJoinPool.commonPool().awaitQuiescence(10, TimeUnit.SECONDS);

这样Maven尝试中断线程时,commonPool的线程已经处于空闲状态,能及时响应中断。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:20:25