将始终执行CompletableFuture回调

时间:2018-09-01 06:43:04

标签: java completable-future

我有两个服务电话:

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();

从运行我的示例代码来看,它们似乎都是相同的。但是我觉得某处可能有问题。谢谢!

2 个答案:

答案 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)

supplyAsyncthenAccept方法应根据文档在单独的线程上自动执行:

  

所有没有显式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完成时结束。这在功能上也应该与第一个相同。