Java 线程池中 shutdown 与 shutdownNow 的区别是什么?

Sherwin.Wei Lv7

Java 线程池中 shutdown 与 shutdownNow 的区别是什么?

重点回答

shutdown()shutdownNow() 都用于关闭线程池,但工作方式有所不同:

1)**shutdown()**:

启动线程池的平滑关闭。它不再接受新的任务,但会继续执行已经提交的任务(包括在队列中的任务)。

线程池会进入 SHUTDOWN 状态,所有已执行和正在执行的任务都会继续完成,只有所有任务完成后,线程池才会完全终止。

2)**shutdownNow()**:

启动线程池的强制关闭。它会尝试停止所有正在执行的任务,并返回等待执行的任务列表。它会尽力中断正在执行的任务,但不能保证所有任务都能被立即停止

线程池进入 STOP 状态,除了尝试中断正在执行的任务外,还会清空任务队列,返回未执行的任务列表。

使用场景

  • **shutdown()**:适用于程序需要平滑停止线程池的场景,如应用程序正常退出时。保证所有已提交的任务都能执行完毕,避免任务丢失。
  • **shutdownNow()**:适用于紧急情况或需要立即停止线程池的场景,如应用程序异常退出时。快速清理资源,但可能导致部分任务未完成。

扩展知识

shutdownNow 的尝试中断

shutdownNow() 会通过调用 Thread.interrupt() 来中断线程,但这取决于任务实现的具体中断响应方式。如果任务在执行过程中没有正确处理中断(如未检查 Thread.interrupted() 状态),则无法强制中断。

image.png

还有,使用 shutdownNow() 时,返回的任务列表包含所有未执行的任务。我们可以选择将这些任务重新提交到另一个线程池或进行其他处理(日志记录、落库等等)。

线程池生命周期

  • 运行状态(RUNNING):线程池可以接受新任务并处理已提交的任务。
  • 关闭状态(SHUTDOWN):通过调用 shutdown() 进入,该状态下线程池不再接受新任务,但会继续执行已有的任务。
  • 停止状态(STOP):通过调用 shutdownNow() 进入,该状态下线程池会试图中断所有正在执行的任务并清空任务队列。
  • 终止状态(TERMINATED):所有任务执行完毕且线程池完全关闭后,线程池进入 TERMINATED 状态。

结合 awaitTermination

无论是 shutdown() 还是 shutdownNow(),可以配合 awaitTermination() 方法等待线程池完全终止。awaitTermination() 会阻塞调用线程,直到线程池终止或超时。

比如以下的使用方式:

1
2
3
4
5
6
7
8
9
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}

这种组合方式常用于确保线程池能够在合理时间内关闭,避免无限等待或资源泄漏。

多次调用 shutdown()shutdownNow() 会怎样?

调用 shutdown()shutdownNow() 后,再次调用它们不会有额外效果。shutdownNow() 只会在第一次调用时有效果。

并且,即使线程池进入 SHUTDOWN 状态,相关资源不会立即释放。必须等待所有线程完成任务,线程池进入 TERMINATED 状态后,资源才会释放。

Comments