【问题标题】:Await completion of all ExecutorService Threads等待所有 ExecutorService 线程完成
【发布时间】:2021-08-23 09:40:56
【问题描述】:

我正在尝试掌握 Java 的并发性,我编写了这个简单的代码来打印字母表中的字母:

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
    
    final ExecutorService threadPool = Executors.newFixedThreadPool(4);
    final ExecutorCompletionService<Character> completionService = new ExecutorCompletionService<>(threadPool);

    final List<Character> letters = IntStream.range(65, 91).mapToObj(i -> (char) i).collect(Collectors.toList());

    for (char letter : letters) {
      completionService.submit(() -> printLetter(letter));
    }
    
    System.out.println("Starting shutdown");

    threadPool.shutdown(); // I WAS EXPECTING CODE TO STOP HERE, WAITING FOR ALL THREADS COMPLETION
    
    System.out.println("Ending shutdown");

  }

private static char printLetter(char letter) {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("Hello from: " + letter);
    return letter;
}

当我执行上面的代码时,我希望代码在运行“threadPool.shutdown()”时等待先前线程的完成,但是它继续执行其余代码我可以在输出中看到:

Starting shutdown
Ending shutdown
Hello from: B
Hello from: D
....
Hello from: Z

而我想要的输出是:

Starting shutdown
Hello from: B
Hello from: D
....
Hello from: Z
Ending shutdown

我尝试使用 threadPool.awaitTermination(30, TimeUnit.SECONDS) 代替,但由于我忽略的原因,它等待完全完成 30 秒后再继续,即使所有字母都已打印.

如何等待所有线程完成?

【问题讨论】:

标签: java java.util.concurrent


【解决方案1】:

当我执行上面的代码时,我期望代码在运行“threadPool.shutdown()”时等待先前线程的完成

这不会发生,来自 ExecutorService 的文档:

void shutdown()

启动有序关闭,其中先前提交的任务被 执行,但不会接受新任务。调用没有 如果已经关闭,则附加效果。

此方法不等待之前提交的任务完成 执行。使用awaitTermination 来执行此操作。

为了达到你的结果,你需要同时使用shutdown() 首先然后awaitTermination​()

boolean awaitTermination​(long timeout, TimeUnit unit) throws InterruptedException

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

返回: 如果此执行程序终止,则为 true;如果在终止前超时,则为 false

这种行为:

我尝试使用 threadPool.awaitTermination(30, TimeUnit.SECONDS) 相反,但由于我忽略的原因,它等待完全完成 继续前 30 秒的时间,即使所有字母都已 打印出来的。

可能是由于替换 shutdown()awaitTermination​() 造成的。它正在等待“非关闭”池。如前所述,您需要在awaitTermination​() 之前调用shutdown()

【讨论】:

    【解决方案2】:

    Answer by MarcoLucidi 是正确的,应该被接受。这里还有一些想法。

    不要将执行器服务视为线程池

    你说:

    我期待代码在运行“threadPool.shutdown()”时等待先前线程的完成,

    ExecutorService threadPool

    这些表明您将执行程序服务视为一个线程池。你不应该。 Executors 框架的发明是为了让您摆脱管理线程的繁琐工作。

    关于ExecutorService... “服务”这个词的意义在于它的工作在你不知道或不关心细节的情况下完成。另一个词“执行者”是指它的工作是执行你的Runnable/Callable 任务。没有提及“线程”是故意的。

    因此,在建立 ExecutorService 对象后,您不应再考虑线程池。线程的唯一关注点与您跨线程访问资源的任务有关。

    随着作为 Project Loom 的一部分开发的 虚拟线程 的到来,这一点在未来将更加重要。

    织机项目

    Project Loom 正在开展工作,以极大地增强 Java 中的并发设施。实验版本是available now,基于早期访问Java 18

    AutoCloseable

    一个方便的改进是制作ExecutorService AutoCloseable。这意味着我们可以使用try-with-resources 语法。

    当您分配的所有任务都完成后,自动关闭会处理执行器服务及其后备线程池的关闭。您无需调用shutdownawaitTermination 方法。

    你的代码会更简单更明显。

    try (
        ExecutorService executorService = Executors.… ;
    ) {
        // Submit your `Runnable`/`Callable` tasks to the executor service.
        …
    }
    // At this point, flow-of-control blocks until all submitted tasks are done/canceled/failed.
    // After this point, the executor service will have been automatically shutdown, wia `close` method called by try-with-resources syntax.
    

    【讨论】:

      猜你喜欢
      • 2015-02-14
      • 2018-09-22
      • 2019-10-24
      • 1970-01-01
      • 2021-09-20
      • 2011-03-17
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多