如何正确实现运行多次迭代并等待所有任务完成并在任务完成后成功终止的执行器

Joh*_*ohn 1 java multithreading

切入正题简短回答---------------------

可以在此处找到演示已接受答案的代码:

完整示例:

https://github.com/NACHC-CAD/thread-example/tree/shutdown-first

执行:

https://github.com/NACHC-CAD/thread-example/blob/shutdown-first/src/main/java/com/nachc/examples/threadexample/WidgetFactory.java

原帖--------------------------------------

有许多使用 Java 线程和执行器的示例: https://www.baeldung.com/thread-pool-java-and-guava

https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html

https://howtodoinjava.com/java/multi-threading/java-thread-pool-executor-example/

https://jenkov.com/tutorials/java-concurrency/thread-pools.html

https://xperti.io/blogs/thread-pools-java-introduction/

https://www.journaldev.com/1069/threadpoolexecutor-java-thread-pool-example-executorservice

https://stackify.com/java-thread-pools/

但是,我无法成功编写一个执行所有任务、等待任务完成然后正确终止的示例。

从这个例子开始:https://howtodoinjava.com/java/multi-threading/java-thread-pool-executor-example/

该代码仅调用 executor.shutdown()。如果线程消耗任何时间,则不允许它们有时间完成。

我在这里创建了一个完整的最简单的示例:https ://github.com/NACHC-CAD/thread-example/tree/await-termination

仅关闭分支涵盖了此用例(https://github.com/NACHC-CAD/thread-example/tree/shutdown-only):

public void makeWidgets() {
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(batchSize);
    log.info("Building " + howMany + " widgets...");
    for (int i = 0; i < howMany; i++) {
        Widget widget = new Widget(lotNumber, i);
        WidgetRunnable runnable = new WidgetRunnable(widget);
        executor.execute(runnable);
    }
    log.info("SHUTTING DOWN----------------");
    executor.shutdown();
}
Run Code Online (Sandbox Code Playgroud)

此代码给出以下输出(应该创建 1000 个小部件,并且它们应该在等待 1 秒后报告已完成)。

2022-04-23 21:27:05,796 21:27:05.796 [main] INFO  (WidgetFactoryIntegrationTest.java:12) - Starting test...
2022-04-23 21:27:05,799 21:27:05.799 [main] INFO  (WidgetFactory.java:29) - Building 100 widgets...
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-2] INFO  (Widget.java:24) - Starting build: 1/1
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-4] INFO  (Widget.java:24) - Starting build: 1/3
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-1] INFO  (Widget.java:24) - Starting build: 1/0
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-5] INFO  (Widget.java:24) - Starting build: 1/4
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-6] INFO  (Widget.java:24) - Starting build: 1/5
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-7] INFO  (Widget.java:24) - Starting build: 1/6
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-8] INFO  (Widget.java:24) - Starting build: 1/7
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-10] INFO  (Widget.java:24) - Starting build: 1/9
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-9] INFO  (Widget.java:24) - Starting build: 1/8
2022-04-23 21:27:05,801 21:27:05.801 [main] INFO  (WidgetFactory.java:35) - SHUTTING DOWN----------------
2022-04-23 21:27:05,800 21:27:05.800 [pool-1-thread-3] INFO  (Widget.java:24) - Starting build: 1/2
2022-04-23 21:27:05,801 21:27:05.801 [main] INFO  (WidgetFactoryIntegrationTest.java:18) - Done.
Run Code Online (Sandbox Code Playgroud)

如果我添加 executor.awaitTermination,代码将运行所有线程但永远不会终止。此示例位于等待终止分支中:https ://github.com/NACHC-CAD/thread-example/tree/await-termination

public void makeWidgets() {
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(batchSize);
    log.info("Building " + howMany + " widgets...");
    for (int i = 0; i < howMany; i++) {
        Widget widget = new Widget(lotNumber, i);
        WidgetRunnable runnable = new WidgetRunnable(widget);
        executor.execute(runnable);
    }
    try {
        executor.awaitTermination(1000, TimeUnit.HOURS);
    } catch(Exception exp) {
        throw(new RuntimeException(exp));
    }
    log.info("SHUTTING DOWN----------------");
    executor.shutdown();
}
Run Code Online (Sandbox Code Playgroud)

此代码让所有可运行程序完成但永远不会退出。如何让所有可运行程序完成并使代码运行完成(退出)?

Iro*_*uca 5

参考ThreadPoolExecutor文档。waitTermination() 方法描述如下:

在关闭请求后阻塞,直到所有任务完成执行

shutdown() 方法描述如下

启动有序关闭,执行先前提交的任务,但不会接受新任务

这表明awaitTermination()调用在shutdown()调用之后有效。

为了解决上述问题,需要先调用shutdown(),然后调用awaitTermination()

注意:我没有亲自测试过这一点;然而,正如原帖评论中提到的,约翰已经这样做了,并且该机制有效