在编写Java CompletableFutures时使用哪个执行程序?

我在某个存储库类上有一个返回CompletableFuture 。 完成这些期货的代码使用阻止的第三方库。 我打算有一个单独的有界Executor ,这个存储库类将用它来进行这些阻塞调用。

这是一个例子:

 public class PersonRepository { private Executor executor = ... public CompletableFuture create(Person person) {...} public CompletableFuture delete(Person person) {...} } 

我的应用程序的其余部分将组成这些未来,并对结果做一些其他事情。 当提供给thenAcceptthenComposewhenComplete等的其他函数时,我不希望它们在存储库的Executor上运行。

另一个例子:

 public CompletableFuture replacePerson(Person person) { final PersonRepository repo = getPersonRepository(); return repo.delete(person) .thenAccept(existed -> { if (existed) System.out.println("Person deleted")); else System.out.println("Person did not exist")); }) .thenCompose(unused -> { System.out.println("Creating person"); return repo.create(person); }) .whenComplete((unused, ex) -> { if (ex != null) System.out.println("Creating person"); repo.close(); }); } 

JavaDoc声明:

为非异步方法的依赖完成提供的动作可以由完成当前CompletableFuture的线程执行,或者由完成方法的任何其他调用者执行。

附带问题:为什么还有这里? 在什么情况下,还有另一个完成方法的调用者无法完成当前的未来?

主要问题:如果我希望所有println都由不同的Executor不是存储库使用的Executor ,我需要哪些方法进行异步并手动提供执行程序?

显然thenAccept需要改为thenAcceptAsync但我不确定那一点。

替代问题:哪个线程从thenCompose完成返回的未来?

我的猜测是,无论线程完成从函数参数返回的未来是什么。 换句话说,我还需要将whenComplete更改为whenCompleteAsync

也许我过度复杂化了,但这感觉它可能变得非常棘手。 我需要非常关注所有这些未来的来源。 同样从设计的角度来看,如果我回到未来,我如何阻止呼叫者使用我的执行者? 感觉它破坏了封装。 我知道Scala中的所有转换函数都采用隐式ExecutionContext ,它似乎解决了所有这些问题。

方问:如果您将中间CompletionStage分配给变量并在其上调用方法,它将在同一个线程上执行。

主要问题:只有第一个,所以改变thenAccept接受thenAcceptAsync – 所有以下的将在用于接受的线程上执行它们的步骤。

备选问题:从thenCompose完成未来的线程与用于撰写的线程相同。

您应该将CompletionStages视为在同一线程上快速连续执行的步骤(通过按顺序应用函数),除非您特别希望使用异步在不同的线程上执行该步骤。 然后,在该新线程上完成所有后续步骤。

在您当前的设置中,步骤将执行如下:

 Thread-1: delete, accept, compose, complete 

第一个接受异步,它变为:

 Thread-1: delete Thread-2: accept, compose, complete 

关于你的最后一个问题,关于调用者使用的相同线程,如果他们添加了额外的步骤 – 我认为除了不返回CompletableFuture之外你还能做些什么,而不是正常的Future

只是从我的经验观察中来看,执行这些非异步方法的线程将取决于首先发生的, thenCompose自己或者Future背后的任务。

如果thenCompose首先完成(在您的情况下,几乎可以肯定),那么该方法将在执行Future任务的同一线程上运行。

如果你的Future背后的任务首先完成,那么该方法将立即在调用线程上运行(即根本没有执行程序)。