【问题标题】:Own ExecutorService used to create CompletableFuture does not terminate用于创建 CompletableFuture 的自己的 ExecutorService 不会终止
【发布时间】:2021-01-19 13:18:38
【问题描述】:

我正在尝试使用我自己的ExecutorService 创建一组CompletableFutures 来链接多个流程步骤。这些步骤可能会引发异常。

当他们这样做时,在我看来,ExecutorService 中的线程没有被释放,尽管我正在尝试处理这种情况。

class Scratch {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);

        AtomicInteger counter = new AtomicInteger();
        Supplier<?> throwingException = () -> { throw new RuntimeException("throw " + counter.incrementAndGet()); };
        Function<String, CompletableFuture<?>> process =
                url -> CompletableFuture.supplyAsync(throwingException, executor)
                        .exceptionally(Scratch::log);
        var collect = IntStream.range(1, 10).mapToObj(i -> "url" + i)
                .map(process)
                .toArray(CompletableFuture[]::new);
        final CompletableFuture<Void> together = CompletableFuture.allOf(collect);
        System.out.println("joining");
        together.exceptionally(Scratch::log).join();
        System.out.println("finished");
        if (executor.awaitTermination(5, TimeUnit.SECONDS)) {
            System.out.println("exiting cleanly");
        } else {
            System.out.println("not terminated");
        }
        executor.submit(() -> System.out.println("still executing"));
    }
    static <T> T log(Throwable t) {
        System.out.println(t.getMessage());
        return null;
    }
}

输出是

java.lang.RuntimeException: throw 1
joining
java.lang.RuntimeException: throw 2
java.lang.RuntimeException: throw 3
java.lang.RuntimeException: throw 4
java.lang.RuntimeException: throw 5
java.lang.RuntimeException: throw 6
java.lang.RuntimeException: throw 7
java.lang.RuntimeException: throw 8
java.lang.RuntimeException: throw 9
finished
not terminated

由此启动的进程也没有终止(这是我注意到的)。

在我看来,这应该意味着此时ExecutorService 中没有留下任何线程,但情况似乎并非如此;如果我们降低线程池容量,它仍然会运行所有提交的任务,如果我们在失败终止后添加另一个提交(例如executor.submit(() -&gt; System.out.println("still executing"));),它将被执行。

如果我们不将自己的ExecutorService 传递给CompletableFutre::supplyAsync,进程将按预期终止。

我也尝试过处理异常状态的其他版本(例如使用together.whenComplete()),但结果相同。

为什么会发生这种情况,如何确保ExecutorService 正确终止?

编辑:我意识到这不是导致问题的异常,这将发生在使用您自己的执行程序服务提供给CompletableFuture 的任何任务中,鉴于 Eugene 的回复,这完全有道理。我正在更改问题标题。

【问题讨论】:

    标签: java executorservice java.util.concurrent completable-future


    【解决方案1】:

    这里发生了两件事。第一个是当您执行没有显式Executor 时,您的操作将在ForkJoinPool 中运行。该池使用守护线程,它不会阻止 VM 退出。所以当你的main结束时,VM就存在了。

    第二点在awaitTermination的文档中,其实是:

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

    由于您没有调用shutDown,并且该池创建了非守护进程线程,因此进程不会退出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      相关资源
      最近更新 更多