使用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
相关产品推荐
相关产品推荐

