CompletableFuture:为什么我们需要阶段?

CompletableFuture: Why we need stages at all?

我做了研究,但没有找到适合这个问题的答案。

为什么我们需要比舞台更多的舞台。

一个线程 -> 一个大任务(A,B,C,D) VS 具有 A、B、C、D 阶段的 CompletableFuture

所以我的回答如下:

我的最后一点有没有 metrics/rules,我应该将大任务分成多小的子任务?

当你有选择的时候,喜欢

CompletableFuture.supplyAsync(() -> method1())
    .thenApply(o1 -> method2(o1))
    .thenApply(o2 -> method3(o2))
    .thenAccept(o3 -> method4(o3));

CompletableFuture.runAsync(() -> {
    var o1 = method1();
    var o2 = method2(o1);
    var o3 = method3(o2);
    method4(o3);
});

CompletableFuture.runAsync(() -> method4(method3(method2(method1()))));

使用多个阶段没有任何优势。事实上,第一个变体比其他变体更难调试。

当链接不发生在同一个地方时,情况就不同了。想象一个库有一个未来的返回方法,封装类似 supplyAsync(() -> method1()) 的东西,另一个库调用该方法,链接另一个操作并将组合返回给将链接另一个应用程序的应用程序。

只有当函数中调用的方法仍然由每个库的 API 提供并且具有顺序性质时,才有可能在单个阶段表达相同的内容,即我们不是在谈论 thenCompose(…) 阶段类型。

但是这样的链仍然很难调试,项目 Loom 正试图解决这个问题。然后,您将操作表达为一个调用序列,就像在第二个或第三个变体中一样,即使方法可能会阻塞,但 运行 它在一个虚拟线程中,每次它都会释放底层本机线程会阻塞。

然后,我们对线性阶段链的使用就更少了。


创建依赖阶段的线性链的剩余用例是具有不同的执行器。例如

CompletableFuture.supplyAsync(() -> fetchFromDb(), MY_BACKGROUND_EXECUTOR)
    .thenAcceptAsync(data -> updateSwingModel(data), EventQueue::invokeLater)
    .whenCompleteAsync((x, thrown) ->
         updateStatusBar(jobID, thrown), EventQueue::invokeLater);

在这里,将操作写成单个块不是一种选择……