CompletableFuture.supplyAsync() 中 ForkJoinPool 的行为

Behaviour of ForkJoinPool in CompletableFuture.supplyAsync()

我正在比较 CompletableFuture.supplyAsync() 在我设置自定义 ExecutorService 的两种情况下的行为或者我希望我的供应商由默认执行程序(如果未指定)执行,即 ForkJoinPool.commonPool()

让我们看看区别:

public class MainApplication {
  public static void main(final String[] args) throws ExecutionException, InterruptedException {

    Supplier<String> action1 = () -> {
        try {
            Thread.sleep(3000);
        }finally {
            return "Done";
        }
    };
    Function<String, String> action2 = (input) -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            return input + "!!";
        }
    };

    final ExecutorService executorService = Executors.newFixedThreadPool(4);

    CompletableFuture.supplyAsync(action1, executorService)
                     .thenApply  (action2)
                     .thenAccept (res -> System.out.println(res));

    System.out.println("This is the end of the execution");

  }
}

在这种情况下,我将 executorService 传递给我的 supplyAsync() 并打印:

This is the end of the execution

Done!!

所以主执行结束后打印“完成”。

但是如果我改用:

CompletableFuture.supplyAsync(action1)

所以我没有传递我的自定义 executorService 和 CompletableFuture class 在后台使用 ForkJoinPool.commonPool() 然后“完成”不是全部打印:

This is the end of the execution

Process finished with exit code 0

为什么?

ForkJoinPool 使用不阻止 JVM 退出的守护线程。另一方面,由 Executors 创建的 ExecutorService 中的线程是非守护线程,因此它可以防止 JVM 退出,直到您明确关闭线程池。

另请注意,在您的示例中,您需要在最后关闭池以终止 JVM。

executorService.shutdown();

因此,一种解决方案是让主线程等待几秒钟,直到您的计算完成,

Thread.sleep(4000);

在这两种情况下

CompletableFuture.supplyAsync(action1, executorService)
                     .thenApply  (action2)
                     .thenAccept (res -> System.out.println(res));

您无需等待任务完成。但是随后您的程序将要退出,并且常见的 fork join pool 存在差异:

ForkJoinPool.commonPool()

和常规执行服务:

final ExecutorService executorService = Executors.newFixedThreadPool(4);

..对尝试调用 System.exit(...) 等价物做出反应。

这就是doc says关于fork join common pool的内容,请注意:

However this pool and any ongoing processing are automatically terminated upon program System.exit(int). Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.

也就是link到ExecutorService docs,你可以点关注:

The shutdown() method will allow previously submitted tasks to execute before terminating

我认为这可能与您询问的不同。