在两种情况下,我比较 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()并打印:
这是执行的结束
完成!
因此“完成”将在主要执行结束后打印出来。
但如果我改用:
CompletableFuture.supplyAsync(action1)
所以我不通过我的自定义executorService,而CompletableFuture类在 ForkJoinPool.commonPool()的幕后使用,则根本不打印“完成”:
这是执行的结束
以退出代码0结束的过程
为什么?
答案 0 :(得分:1)
ForkJoinPool
使用的守护进程线程不会阻止JVM退出。另一方面,由Executors创建的ExecutorService中的线程是非守护程序线程,因此,在您显式关闭线程池之前,它可以防止JVM退出。
还要注意,在您的示例中,您需要在最后关闭池以终止JVM。
executorService.shutdown();
因此,一种解决方案是让主线程等待几秒钟,直到像这样完成计算为止,
Thread.sleep(4000);
答案 1 :(得分:1)
两种情况下
CompletableFuture.supplyAsync(action1, executorService)
.thenApply (action2)
.thenAccept (res -> System.out.println(res));
您不必等待任务完成。但是然后您的程序将退出,并且普通的fork连接池的方式有所不同:
ForkJoinPool.commonPool()
和常规执行服务:
final ExecutorService executorService = Executors.newFixedThreadPool(4);
..在尝试调用等效于System.exit(...)时反应。
这是关于{fork join common pool的doc says的内容,您应该引起注意:
但是此池和任何正在进行的处理会自动进行 在程序System.exit(int)上终止。任何依赖的程序 异步任务处理在程序终止之前完成 退出前应调用commonPool()。awaitQuiescence。
这是ExecutorService docs的链接,您可能需要注意:
shutdown()方法将允许先前提交的任务执行 终止之前
我认为这可能与您要求的有所不同。