文章

线程池 shutdown() 和 shutdownNow() 完整对比

共同点

  1. 拒绝新任务:两者调用后都会立即拒绝新提交的任务,抛出 RejectedExecutionException

  2. 非阻塞调用:两个方法都会立即返回,不会等待所有任务结束

  3. 触发关闭流程:都会使线程池开始关闭过程,最终目标是达到 TERMINATED 状态

核心区别

特性

shutdown()

shutdownNow()

关闭方式

温和关闭

急切关闭

线程池状态变化

RUNNING → SHUTDOWN

RUNNING → STOP

正在执行的任务

不中断,等待完成

发送中断信号,尝试停止

等待队列中的任务

继续执行完毕

不执行,从队列中移除并返回

返回值

无返回值

返回未执行的任务列表 List<Runnable>

中断处理

只中断空闲工作线程

中断所有工作线程

任务完成保证

所有已提交任务都会执行

不保证所有任务都能完成

中断机制详解

shutdown() 的中断机制

  • 只中断空闲线程:调用 interruptIdleWorkers()

  • 目的

    • 释放空闲线程占用的系统资源

    • 加速线程池的完全终止过程

    • 让空闲线程检测到关闭信号并退出

  • 不中断正在执行任务的线程:确保任务能够完成

shutdownNow() 的中断机制

  • 中断所有线程:调用 interruptWorkers()

  • 目的:尽快停止所有活动,包括正在执行的任务

  • 不保证任务停止:任务需自行处理中断信号

拒绝新任务的机制

在两种方法中,拒绝新任务都是通过改变线程池状态实现的:

  • 状态从 RUNNING 变为 SHUTDOWNSTOP

  • 在这些状态下,execute()submit() 方法会直接拒绝新任务

  • 与工作线程是否被中断无关

使用场景

  • shutdown()

    • 需要所有已提交任务都能完成

    • 有足够时间等待任务执行

    • 需要优雅关闭

  • shutdownNow()

    • 需要紧急停止

    • 需要获取未执行任务列表

    • 资源紧张,不能等待所有任务完成

最佳实践

executorService.shutdown();
try {
    if (!executorService.awaitTermination(timeout, TimeUnit.SECONDS)) {
        List<Runnable> unfinishedTasks = executorService.shutdownNow();
        System.out.println("线程池强制关闭,剩余未执行任务数: " + unfinishedTasks.size());
    }
} catch (InterruptedException e) {
    executorService.shutdownNow();
    Thread.currentThread().interrupt();
}

总结

shutdown()shutdownNow() 的本质区别在于对任务和线程的处理策略:shutdown() 更温和,保证任务完成;shutdownNow() 更激进,优先释放资源。两者都拒绝新任务,但通过不同的中断机制和任务处理方式来实现线程池的关闭。

License:  CC BY 4.0