TL;DR - add ForkJoinPool.commonPool().awaitQuiescence(1000, TimeUnit.SECONDS);
在您致电后CompletableFuture.runAsync
并在代码末尾以便System.exit
不会停止你的可运行程序。这样你就会得到你的行为。
更长的答案:
好的,首先,我在 Oracles java 8、OpenJDK 8 和 OpenJDK 11 中尝试了这两个示例。全面一致的行为,所以我的答案是nothing不同java版本的这些实现发生了变化,这会导致这种差异。在both例如,您看到的行为与 Java 告诉您它将执行的行为一致。
从文档CompletableFuture.runAsync
返回一个新的 CompletableFuture,它由运行在中的任务异步完成ForkJoinPool.commonPool()
在运行给定的操作之后。
好吧...让我们看看是什么ForkJoinPool.commonPool
会告诉我们(强调我的):
返回公共池实例。该池是静态构建的;它的运行状态不受尝试的影响shutdown()
or shutdownNow()
. 然而,该池和任何正在进行的处理都会根据程序自动终止System.exit(int)
。任何程序依赖于异步任务处理在程序终止应调用之前完成commonPool().awaitQuiescence
,退出之前。
啊哈,这就是为什么我们在使用公共池时看不到倒计时,因为公共池将在系统退出时终止,这正是我们从方法返回并退出程序时发生的情况(假设您的示例是正如您所展示的那样,真的很简单...就像调用单个方法一样main
... 无论如何)
那么为什么自定义执行器会起作用呢?因为,正如您已经注意到的那样,该执行程序尚未终止。后台仍然有一段代码在运行,尽管是闲置的,但 Java 没有能力停止。
那么我们现在能做什么呢?
One选择是做我们自己的执行程序并在完成后将其关闭,就像您所建议的那样。我认为这种方法isn't终究不好用。
Second选项是遵循 java doc 的说明。
任何依赖异步任务处理在程序终止之前完成的程序都应该调用commonPool().awaitQuiescence
,退出之前。
公共布尔等待静止(长时间超时,
TimeUnit 单位)
如果由在此池中运行的 ForkJoinTask 调用,则等效于 ForkJoinTask.helpQuiesce()。否则,等待和/或尝试协助执行任务,直到该池 isQuiescent() 或指示的超时过去。
因此,我们可以调用该方法并为公共池中的所有公共进程指定超时。我的看法是,这有点业务特定,从现在开始你必须回答这个问题 -现在超时到底应该是什么?.
Third选项是使用的力量CompletableFuture
s 并吊起这个runAsync
变量的方法:
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> ...
...
...
bla bla bla code bla bla bla
...
...
voidCompletableFuture.join();
// or if you want to handle exceptions, use get
voidCompletableFuture.get();
然后当你需要它的时候,你join()/get()
无论你需要什么作为返回值。我最喜欢这个,因为这样的代码是最干净、最容易理解的。我也可以chain我的 CF 有我想要的一切,并可以用它们做一些时髦的事情。
在这种情况下你不需要返回值,也不需要做任何其他事情,只想要一个简单的字符串返回和从 1 到 20 的异步处理,然后只需推ForkJoinPool.commonPool().awaitQuiescence(1000, TimeUnit.SECONDS);
在你方便的地方并给它一些荒谬的超时,因此保证你将退出所有人idle流程。