ExecutorService 的 shutdown() 不会等到所有线程都完成

ExecutorService's shutdown() doesn't wait until all threads will be finished

我有一个代码,其中同时有 4 个线程 运行。我想等到所有这 4 个线程都完成。只有在那之后才能继续应用程序流程。

我尝试了两种方法:

  1. Thread#join(),此方法按预期工作。 join() 之后的代码仅在所有线程完成后执行。
  2. ExecutorService#shutdown(),即使并非所有线程都已完成,此技术也允许执行 shutdown() 之后的代码。

代码示例:

ExecutorService service = Executors.newFixedThreadPool(cpuCoresNum);

for (int i = 0; i < cpuCoresNum; i++) {

    service.submit(() -> {
        try {
            foo(); // some long execution function
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

service.shutdown();

System.out.println("We're done! All threads are finished!");

我的问题:

答案可在 ExecutorService.shutdown() Javadoc 中找到:

This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.

如果您想等待线程完成工作,您有以下选择:

  • 获取 submit() 返回的 Future 个实例并在每个 Future 个实例上调用 get()
  • service 上调用 shutdown 后,在 service 上调用 awaitTermination 直到 returns true
  • 而不是在 service 上调用 submit 将您的 Runnable 实例添加到 java.util.List 并将此列表传递给在 [= 上调用的 invokeAll 方法17=]

感谢@Adam Siemion的建议,这里是最终代码:

ExecutorService service = Executors.newFixedThreadPool(cpuCoresNum);

int itNum = 1;

for (int i = 0; i < cpuCoresNum; i++) {

    int treadID = itNum++;

    service.submit(() -> {
        Thread.currentThread().setName("Thread_#" + treadID);
        try {
            foo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

// wait until all threads will be finished
service.shutdown();
try {
    service.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
}

来自 ExecutorService 的 oracle 文档页面的推荐方法:

 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();
   }

shutdown(): 启动有序关闭,执行之前提交的任务,但不会接受新任务。

shutdownNow():尝试停止所有正在执行的任务,停止等待任务的处理,并returns列出等待执行的任务。

在上面的示例中,如果您的任务需要更多时间才能完成,您可以将 if 条件更改为 while 条件

替换

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

 while(!pool.awaitTermination(60, TimeUnit.SECONDS)) {
     Thread.sleep(60000);
 }