ScheduledExecutorService调用shutdownNow后如何终止运行中的Runnable
问题本质原因
Java的线程中断机制是协作式通知模型,不存在可安全强制终止运行中线程的方案。
你之前调用的shutdownNow()、scheduledFuture.cancel(true),本质只是给对应工作线程设置一个中断标记,不会直接中断代码执行。这也是为什么只有当任务里有Thread.sleep()这类会主动检测中断标记、抛出InterruptedException的逻辑时,任务才能被正常终止。
当前代码中中断操作完全失效的具体原因:
- 核心计算逻辑是纯CPU循环,全程没有触发可响应中断的阻塞操作,中断标记对这类纯计算代码没有强制打断效果
- 整个任务流程没有主动检查线程的中断状态,就算中断标记已经被设置,代码也会无视标记持续运行直到任务结束
- 异常捕获逻辑会吞掉所有中断感知信号,就算中途出现中断相关异常,也不会触发整体任务退出
可行落地方案
- 在长耗时计算循环中定期主动检查中断状态:每执行固定次数的循环就检查一次
Thread.currentThread().isInterrupted(),检测到中断信号就直接跳出循环终止任务,注意检查频率不要过高,避免过度损耗计算性能 - 在流式处理的流程中增加中断校验:每处理完一个待处理元素,先判断当前线程是否被中断,发现中断信号就直接抛出异常终止整个流处理流程
- 正确传递中断状态:如果捕获到
InterruptedException,不要直接吞掉异常,要调用Thread.currentThread().interrupt()恢复中断标记,保证上层逻辑可以感知到中断信号 - 修正线程池初始化参数:Scheduled线程池的核心线程数不能设置为0,建议至少设置为1,避免调度逻辑出现运行异常
注意:Java已经废弃了
Thread.stop()这类强制终止线程的API,因为这类操作会导致锁状态不一致、资源未释放、数据损坏等严重问题,所有长耗时任务必须自身实现可中断逻辑,才能配合线程池的关闭操作正常退出。
修改后的可运行示例
import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class ThreadWork { public static void main(String[] args) throws InterruptedException { // 修正核心线程数参数,避免0核心线程的调度异常 ScheduledExecutorService schExService = Executors.newScheduledThreadPool(1); int repeatMin = 2; ScheduledFuture<?> scheduledFuture = schExService.scheduleWithFixedDelay(new RunConfiguraiton(), 0, repeatMin, TimeUnit.SECONDS); Thread.sleep(3*1000); scheduledFuture.cancel(true); schExService.shutdownNow(); Thread.sleep(1*1000); System.out.println("I am good there for fresh start"); } } class RunConfiguraiton implements Runnable { @Override public void run() { List<Integer> intList = Arrays.asList(10,5,60,45,100,50,232); try { intList.stream().map(i -> { // 每个元素处理前检查中断状态 if (Thread.currentThread().isInterrupted()) { throw new RuntimeException("流处理被中断,停止任务"); } try { return DoSomeHardJob.adding(i); } catch (Exception e) { e.printStackTrace(); return 0; } }).collect(Collectors.toList()); } catch (Exception e) { System.out.println("final exit"); e.printStackTrace(); } } static class DoSomeHardJob { public static int adding(int i) throws Exception { try { int result = 0; for (int j = 0; j < 999999999; j++) { // 每1000次循环检查一次中断状态,降低性能损耗 if (j % 1000 == 0 && Thread.currentThread().isInterrupted()) { throw new RuntimeException("任务被中断,退出计算"); } System.out.print(j); result = result + j; } return result; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } } }
内容的提问来源于stack exchange,提问作者Srikanth
相关产品推荐
相关产品推荐

