thenRunAsync(...)和CompletableFuture.runAsync(()-> {...});是否完全相关?

时间:2019-07-15 23:38:50

标签: java multithreading java-8 completable-future

我需要执行一些额外的任务,但让原始线程结束,例如 发回HTTP响应。

我想我可以这样做:

return mainTasksFuture.thenApply(response -> {
  CompletableFuture.runAsync(() -> {
    // extra tasks
  });
  return response;
});

但是我记得有一个thenRunAsync。是

return mainTasksFuture.thenApply(response -> {
  return response;
}).thenRunAsync(() -> {
  // extra tasks
});

基本上是做同样事情的另一种方法吗?换句话说,then*Async方法终止符(完成方法)是否在原始线程中返回前一个链的结果,然后产生一个新的线程来执行其余操作?

我几乎可以肯定答案是。只是似乎,对于CompletableFutures的新手来说,可能纯粹基于方法名称。不过,我想得到一个确认,以防万一我读到的ForkJoinPool.commonPool 实际上是在以不同的方式说出我的疑问。

2 个答案:

答案 0 :(得分:1)

runAsyncthenRunAsync都执行Runnable异步任务

  

使用此阶段的默认异步执行工具执行给定操作

问题:换句话说,then * Async方法终止符(完成方法)是否在原始线程中返回前一个链的结果,然后产生一个新的线程来执行其余的? >

答案:否,来自文档一个阶段的执行可能是由一个阶段的完成,两个阶段或两个阶段或两个阶段中的一个触发的。结果可能会根据程序员对该部分的编码方式而返回,但是现在,在您的情况下(使用thenRunAsync,结果将在第一阶段完成后返回,因为在第二阶段thenRunAsync中,您将从第一阶段作为输入,但不返回任何内容。

Interface CompletionStage

  

一个阶段的执行可以由一个阶段的完成,两个阶段的两个或两个阶段中的一个触发。使用带有前缀的方法来安排对单个阶段的依赖。通过两个阶段都完成而触发的结果可以使用相应命名的方法组合其结果或效果。由两个阶段中的任何一个触发的结果不能保证哪个结果或效果用于从属阶段的计算。

第一个示例和第二个示例之间也存在细微差别

示例:1 在本示例中,Runnable任务在返回结果之前被异步执行,Function的{​​{1}}和thenApply的结果Runnable将同时执行

runAsync

示例:2 在此示例中,return mainTasksFuture.thenApply(response -> { CompletableFuture.runAsync(() -> { // extra tasks }); return response; }); 中的Runnable任务将在完成thenRunAsync中的Function后执行。

thenApply

答案 1 :(得分:1)

您写了

  

对于*刚接触CompletableFutures的人来说,*似乎*可能纯粹基于方法名称。

好吧,方法名称正确地反映了方法的作用。 runAsyncthenRunAsync都初始化Runnable的异步执行并返回一个future,该异步执行完成后将完成。因此,在名称上的相似性是合理的。

这是您的代码,根本不同。

在此版本中

return mainTasksFuture.thenApply(response -> {
  CompletableFuture.runAsync(() -> {
    // extra tasks
  });
  return response;
});

您完全忽略了runAsync返回的未来,因此一旦触发了异步操作,thenApply返回的未来将完全完成。在“额外任务”仍同时运行时,调用方可以检索结果值。

相反,

    return mainTasksFuture.thenApply(response -> {
        return response;
    }).thenRunAsync(() -> {
      // extra tasks
    });

thenApply完全过时,因为它什么也没做。但是您返回的是thenRunAsync返回的未来,当Runnable的异步执行完成并且类型为CompletableFuture<Void>时,它将完成,因为runnable不会产生值(将来将以null结束)。在特殊情况下,除了mainTasksFuture之外,它都会完成,但在成功情况下,它不会通过结果值。

如果第一个变体符合您的实际意图(调用者不应依赖于额外任务的完成),只需将它们建模为依赖项即可

mainTasksFuture.thenRunAsync(() -> {
    // extra tasks
});
return mainTasksFuture; // does not depend on the completion of extra task

否则,请保留变体2(减去过时的东西)

return mainTasksFuture.thenRunAsync(() -> {
  // extra tasks
}); // depends on the completion of extra task but results in (Void)null

如果不需要结果值。否则,您可以使用

return mainTasksFuture.thenApplyAsync(response -> {
    // extra tasks
    return response;
}); // depends on the completion of extra task and returns original result

相同
return mainTasksFuture.thenCompose(response ->
    CompletableFuture.runAsync(() -> {
        // extra tasks
    }).thenApply(_void -> response));

这不会忽略runAsync返回的未来,但与thenApplyAsync相比,这种并发症没有任何优势。

另一种选择是

return mainTasksFuture.whenComplete((response,failure) -> {
    if(failure == null) {
        // extra tasks
    }
});

whenComplete返回的未来将以额外的任务完成后的原始未来结果完成。但是,即使原始的未来完成得特别好,该功能也会一直进行评估,因此,如果不需要,则需要另一个条件。