Completable Future发布什么时候会回线到线程池?

时间:2018-01-11 18:28:06

标签: java java-8 threadpool completable-future

CompletableFuture什么时候会将线程发回ThreadPool?是在调用get()方法之后还是在完成相关任务之后?

1 个答案:

答案 0 :(得分:5)

get调用与池中的线程之间没有关系。未来的完成与线程之间甚至没有关系。

CompletableFuture可以从任何地方完成,例如通过调用complete就可以了。当您使用其中一种便捷方法将任务安排到最终尝试完成的执行程序时,那么当完成尝试时,该线程将被用于该点,无论未来是否已经完成。

例如,

CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> "hello");

在语义上等同于

CompletableFuture<String> f = new CompletableFuture<>();

ForkJoinPool.commonPool().execute(() -> {
    try {
        f.complete("hello");
    } catch(Throwable t) {
        f.completeExceptionally(t);
    }
});

很明显,线程池和预定的操作都不关心某人是否在未来调用get()join()

即使您提前完成未来,例如通过complete("some other string")或通过cancel(…),它对正在进行的计算没有影响,因为未来没有对工作的参考。正如the documentation of cancel states

  参数:      

mayInterruptIfRunning - 此值在此实现中无效,因为中断不用于控制处理。

鉴于上述解释,应该清楚原因。

创建依赖关系链时存在差异,例如通过b = a.thenApply(function)。评估function的工作将在a完成之前提交。在a完成时,b的完成状态将在function评估开始之前重新检查。如果此时已完成b,则可能会跳过评估。

CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    return "foo";
});
CompletableFuture<String> b = a.thenApplyAsync(string -> {
    System.out.println("starting to evaluate "+string);
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
    System.out.println("finishing to evaluate "+string);
    return string.toUpperCase();
});
b.complete("faster");
System.out.println(b.join());
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);

只会打印

faster

但是一旦评估开始,它就无法停止,所以

CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    return "foo";
});
CompletableFuture<String> b = a.thenApplyAsync(string -> {
    System.out.println("starting to evaluate "+string);
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
    System.out.println("finishing to evaluate "+string);
    return string.toUpperCase();
});
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
b.complete("faster");
System.out.println(b.join());
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);

将打印

starting to evaluate foo
faster
finishing to evaluate foo

显示即使在您成功检索早期已完成的未来的值时,仍可能有一个仍在运行的后台计算将尝试完成将来。但随后的完成尝试将被忽略。