我有两个服务电话:
String call1() { ... return "ok"; }
void call2(String) { ... }
我知道带回调的CompletableFuture的基本方式是
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
如果我将两个链接的CompletableFuture分开,该怎么办?
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
这与在future2上调用join()有什么不同:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
如果我在两个期货上都调用join()怎么办?
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
从运行我的示例代码来看,它们似乎都是相同的。但是我觉得某处可能有问题。谢谢!
答案 0 :(得分:3)
他们不一样。
简而言之,您可以将其视为future1
并且future2
持有不同任务的结果(即使future2
使用future1
的结果,这也是一个不同的未来)。
future1.join()
将阻塞,直到() -> call1()
结束为止,future2
的任务直到那时才开始。 future2.join()
将等到s -> call2(s)
完成。
如果我将两个链接的CompletableFuture分开,该怎么办?
就任务执行而言,这没有区别。这是样式问题,或者仅在需要分别使用两个将来的对象时才重要。
如果我在两个期货上都调用join()怎么办?
在这种情况下,调用future1.join()
是多余的,因为在两个.join
调用之间您没有做任何事情。如果您想在“任务1完成之后和任务2完成之前”执行一些操作,这将是有意义的。
在这种情况下,只需调用future2.join()
就可以了。
下面的代码段应显示其行为:
public static void main(String[] args) {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> delay());
CompletableFuture<Void> future2 = future1.thenRun(() -> delay());
long start = System.currentTimeMillis();
future1.join();
System.out.println("Future 1 done. Waiting for future 2: "
+ (System.currentTimeMillis() - start));
future2.join();
System.out.println("Future 2 complete: "
+ (System.currentTimeMillis() - start));
}
static void delay() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
实际上,此代码输出:
Future 1 done. Waiting for future 2: 5001
Future 2 complete: 10001
但是当您删除future1.join()
时,输出将变为:
Future 2 complete: 10001
这简单地意味着future1.join()
是多余的,除非您在两个期货的完成之间有要执行的操作
答案 1 :(得分:2)
supplyAsync
和thenAccept
方法应根据文档在单独的线程上自动执行:
所有没有显式Executor参数的异步方法都是使用ForkJoinPool.commonPool()
执行的
在您的示例中,唯一的区别是您的线程由于加入而在继续运行之前等待不同的事件。这是细分:
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
这将一直等到call2
完成,因为那是thenAccept
方法返回的未来。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
这只会等到call1
完成并继续进行。 call2
仍将执行。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
这与第一个相同。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
呼叫future1.join()
在call1
完成时结束,然后future2.join()
在call2
完成时结束。这在功能上也应该与第一个相同。