ScheduledExecutorService调用shutdown后仍运行任务的原因是什么?
问题原因
- 最核心的原因是
cancel(false)的参数配置错误:cancel方法的入参mayInterruptIfRunning设为false时,仅会取消还未开始调度的任务,不会中断已经处于运行状态的任务。你用的是scheduleWithFixedDelay提交的周期性任务,若调用cancel时正好有任务在执行,该任务会正常跑完当前逻辑才会终止,你就会观测到shutdown后仍有Runnable在运行。 scheduledFutures使用了非线程安全的ArrayList:如果存在并发调用execute提交任务、同时触发shutdown的场景,遍历列表时可能出现并发修改异常,甚至部分刚提交的ScheduledFuture没被遍历到,导致对应的任务没有被取消,持续运行。- 任务本身可能不响应中断:就算后续调用
shutdownNow会尝试中断所有运行中的线程,如果你的Runnable逻辑里吞了InterruptedException、没有处理中断标记,中断信号不会生效,任务还是会继续执行。 awaitTermination仅等待1秒:如果正在运行的任务单次执行时长超过1秒,在等待超时前你观测到任务还在运行是符合预期的。
修复方案
- 将
cancel的入参改为true,允许中断正在运行的任务 - 把
scheduledFutures替换为线程安全的CopyOnWriteArrayList,避免并发操作异常 - 确保业务
Runnable逻辑正确响应中断,不要吞中断异常 - 可选:开启线程池的
setRemoveOnCancelPolicy配置,cancel任务后自动从队列移除,避免资源占用
修改后的参考代码:
public class ThreadPool { private final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; // 替换为线程安全容器 private final List<ScheduledFuture<?>> scheduledFutures = new CopyOnWriteArrayList<>(); public ThreadPool(ScheduledThreadPoolExecutor scheduledThreadPoolExecutor) { this.scheduledThreadPoolExecutor = scheduledThreadPoolExecutor; // 可选配置:任务被cancel后自动从调度队列移除 this.scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); } public void execute(Runnable runnable) { scheduledFutures.add( scheduledThreadPoolExecutor.scheduleWithFixedDelay(runnable, 0, 20, TimeUnit.SECONDS)); } public void shutdown() { // 改为传true,允许中断运行中的任务 scheduledFutures.forEach(scheduledFuture -> scheduledFuture.cancel(true)); // 可选:cancel后清空列表,避免重复操作、内存泄露 scheduledFutures.clear(); scheduledThreadPoolExecutor.shutdown(); try { // 可以根据业务任务的最大执行时长调整等待时间 if (scheduledThreadPoolExecutor.awaitTermination(3, TimeUnit.SECONDS)) { return; } scheduledThreadPoolExecutor.shutdownNow(); } catch (Exception exception) { Thread.currentThread().interrupt(); } } }
内容的提问来源于stack exchange,提问作者Zafiro Geta
相关产品推荐
相关产品推荐

