CompletableFuture:为什么我们需要阶段?
CompletableFuture: Why we need stages at all?
我做了研究,但没有找到适合这个问题的答案。
为什么我们需要比舞台更多的舞台。
一个线程 -> 一个大任务(A,B,C,D)
VS
具有 A、B、C、D 阶段的 CompletableFuture
所以我的回答如下:
- 如果我有更多阶段,我可以将任务拆分为不同的方法,类
- 如果我有更多的阶段,执行与其他整个任务相关的整个任务会更公平。我的意思是什么?假设我们的系统中只有一个线程。如果我以这种方式执行它 -> 一个大任务(A、B、C、D),那么在第一个大任务准备好之后,我的下一个大任务(W、X、Y、Z)就有机会被执行。有了 CompletionStages,就更公平了:因为 A、W、B、C、X、Y、Z、D 可能是执行顺序
我的最后一点有没有 metrics/rules,我应该将大任务分成多小的子任务?
- 我的最后一点是 CompletableFutures 阶段的要点吗?
- 我的第一点是
CompletableFutures 中的阶段点?
- 使用CompletableFutures的阶段还有其他的要点吗?
当你有选择的时候,喜欢
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);
在这里,将操作写成单个块不是一种选择……
我做了研究,但没有找到适合这个问题的答案。
为什么我们需要比舞台更多的舞台。
一个线程 -> 一个大任务(A,B,C,D) VS 具有 A、B、C、D 阶段的 CompletableFuture
所以我的回答如下:
- 如果我有更多阶段,我可以将任务拆分为不同的方法,类
- 如果我有更多的阶段,执行与其他整个任务相关的整个任务会更公平。我的意思是什么?假设我们的系统中只有一个线程。如果我以这种方式执行它 -> 一个大任务(A、B、C、D),那么在第一个大任务准备好之后,我的下一个大任务(W、X、Y、Z)就有机会被执行。有了 CompletionStages,就更公平了:因为 A、W、B、C、X、Y、Z、D 可能是执行顺序
我的最后一点有没有 metrics/rules,我应该将大任务分成多小的子任务?
- 我的最后一点是 CompletableFutures 阶段的要点吗?
- 我的第一点是 CompletableFutures 中的阶段点?
- 使用CompletableFutures的阶段还有其他的要点吗?
当你有选择的时候,喜欢
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);
在这里,将操作写成单个块不是一种选择……