如何正确关闭Java的ExecutorService

21

我有一个简单的Java ExecutorService,它运行一些任务对象(实现Callable接口)。

ExecutorService exec = Executors.newSingleThreadExecutor();
List<CallableTask> tasks = new ArrayList<>();
// ... create some tasks
for (CallableTask task : tasks) {
 Future future = exec.submit(task);
 result = (String) future.get(timeout, TimeUnit.SECONDS);
 // TASKS load some classes and invoke their methods (they may create additional threads)
 // ... catch interruptions and timeouts
}
exec.shutdownNow();

当所有任务(要么已完成,要么超时)完成后,我尝试关闭执行器,但它无法停止:exec.isTerminated() = FALSE. 我怀疑一些超时的任务没有被正确终止。

是的,我知道执行器的关闭不能保证任何东西:

除了尽力尝试停止正在处理的活动任务外,没有其他保证。例如,典型的实现将通过{@link Thread#interrupt}取消,因此任何未能响应中断的任务可能永远不会终止。

我的问题是,有没有一种方法可以确保这些(任务)线程将终止? 我想到的最好解决方案是在程序末尾调用System.exit(),但那样很愚蠢。


这个回答解决了你的问题吗?如何关闭ExecutorService? - bric3
2个回答

29

来自Oracle API文档页面的推荐方法-ExecutorService

 void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }

如果您的游泳池需要更长时间才能关闭,您可以进行更改

if (!pool.awaitTermination(60, TimeUnit.SECONDS))
while (!pool.awaitTermination(60, TimeUnit.SECONDS))

关于关闭线程池的方法简要概述

shutdown():

启动一个有序的关闭过程,在此过程中已经提交的任务将被执行,但不会接受新的任务。

shutdownNow():

试图停止所有正在执行的任务,暂停等待执行的任务的处理,并返回等待执行的任务的列表。

awaitTermination(long timeout, TimeUnit unit) throws InterruptedException:

在关闭请求后,阻塞直到所有任务完成执行,或者超时发生,或者当前线程被中断,以先发生的为准。


8

您是否对这些任务有控制?即,您是自己创建这些任务吗?我怀疑在其中的某个地方可能会忽略线程中断,例如:

try {
  ....
}
catch {InterruptedException e) {
   // do nothing
}

当抛出InterruptedException时,需要重置线程上的中断标志,否则线程将无法退出。更多信息请参见此处
不幸的是,您可能正在使用不遵守此规则的库,在这种情况下,您无法轻易地规避此问题。在这种情况下,一个笨重的选择是派生一个子进程来执行 Callable 的工作,并在进程退出时清除所有资源。这很笨重,但可靠。

我忘了提到,那些任务可以自由地调用自定义线程,因此我需要一个解决方案来终止该任务创建的所有内容... - N10
@N10 - 是的。你需要在你的任务周围进行一些生命周期管理。 - Brian Agnew
问题在于,任务实际上会加载其他类并调用它们的方法(这些方法可以是任何东西)。我需要一种方法来终止这些(被调用的方法)线程。我以为超时会终止它们,但似乎不是这样... - N10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接