gpt4 book ai didi

java - 作者使用 thenCompose 而不是 thenComposeAsync 的原因是否正确

转载 作者:行者123 更新时间:2023-12-01 10:29:23 29 4
gpt4 key购买 nike

这个问题和这个问题不一样Difference between Java8 thenCompose and thenComposeAsync因为我想知道作者使用thenCompose的原因是什么而不是 thenComposeAsync .
我正在阅读《现代 Java 实战》,在第 405 页上遇到了这部分代码:

public static List<String> findPrices(String product) {
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Shop> shops = Arrays.asList(new Shop(), new Shop());
List<CompletableFuture<String>> priceFutures = shops.stream()
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
.map(future -> future.thenApply(Quote::parse))
.map(future -> future.thenCompose(quote ->
CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)))
.collect(toList());
return priceFutures.stream()
.map(CompletableFuture::join).collect(toList());
}
一切正常,我可以理解这段代码,但这是作者不使用 thenComposeAsync 的原因。在第 408 页,我无法理解:

In general, a method without the Async suffix in its name executesits task in the same threads the previous task, whereas a methodterminating with Async always submits the succeeding task to thethread pool, so each of the tasks can be handled by adifferent thread. In this case, the result of the secondCompletableFuture depends on the first,so it makes no difference tothe final result or to its broad-brush timing whether you compose thetwo CompletableFutures with one or the other variant of this method


在我与 thenCompose的理解(和 thenComposeAsync)签名如下:
public <U> CompletableFuture<U> thenCompose(
Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(null, fn);
}

public <U> CompletableFuture<U> thenComposeAsync(
Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(asyncPool, fn);
}
第二个 CompletableFuture的结果可以取决于以前的 CompletableFuture在许多情况下(或者我可以说几乎总是),我们应该使用 thenCompose而不是 thenComposeAsync在那些情况下?
如果我们在第二个 CompletableFuture 中有阻塞代码怎么办? ?
这是一个类似的例子,由在这里回答类似问题的人给出: Difference between Java8 thenCompose and thenComposeAsync
public CompletableFuture<String> requestData(Quote quote) {
Request request = blockingRequestForQuote(quote);
return CompletableFuture.supplyAsync(() -> sendRequest(request));
}
我认为在这种情况下使用 thenComposeAsync可以让我们的程序更快,因为这里 blockingRequestForQuote可以在不同的线程上运行。但是根据作者的意见,我们不应该使用 thenComposeAsync因为它取决于第一个 CompletableFuture结果(即报价)。
我的问题是:
作者的想法是否正确,他说:

In this case, the result of the secondCompletableFuture depends on the first,so it makes no difference tothe final result or to its broad-brush timing whether you compose thetwo CompletableFutures with one or the other variant of this method

最佳答案

TL;DR 在这里使用 thenCompose 而不是 thenComposeAsync 是正确的,但不是因为引用的原因。通常,代码示例不应用作您自己代码的模板。

本章是 Stackoverflow 上反复出现的主题,原因我们可以最好地描述为“质量不足”,以保持礼貌。

In general, a method without the Async suffix in its name executes its task in the same threads the previous task, …


规范中对执行线程没有这样的保证。 documentation 说:
  • Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.

因此,任务也有可能“由完成方法的任何其他调用者”执行。一个直观的例子是
CompletableFuture<X> f = CompletableFuture.supplyAsync(() -> foo())
.thenApply(f -> f.bar());
涉及到两个线程。一个调用 supplyAsyncthenApply ,另一个调用 foo() 。如果第二个线程在第一个线程进入 foo() 的执行之前完成了 thenApply 的调用,则有可能 future 已经完成了。
future 不记得哪个线程完成了它。它也没有一些神奇的能力来告诉该线程执行某个操作,尽管它可能正忙于其他事情,甚至从那时起就已终止。所以很明显,在已经完成的 future 上调用 thenApply 不能保证使用完成它的线程。大多数情况下,它会在调用 thenApply 的线程中立即执行操作。规范的措辞“完成方法的任何其他调用者”涵盖了这一点。
但这并不是故事的结局。正如 this answer 解释的那样,当涉及两个以上的线程时,该操作也可以由另一个线程同时在 future 调用无关的完成方法来执行。这可能很少发生,但在引用实现中是可能的,并且是规范允许的。
我们可以将其总结为: 没有 Async 的方法对将执行操作的线程提供最少的控制,甚至可能在调用线程中正确执行,从而导致同步行为。
因此,当正在执行的线程无关紧要并且您不希望后台线程执行时,它们是最好的,即简短的非阻塞操作。

whereas a method terminating with Async always submits the succeeding task to the thread pool, so each of the tasks can be handled by a different thread. In this case, the result of the second CompletableFuture depends on the first, …


当你做
future.thenCompose(quote ->
CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))
涉及到三个 future ,所以还不是很清楚,哪个 future 是“第二个”的意思。 supplyAsync 正在提交一个 Action 并返回一个 future 。提交包含在传递给 thenCompose 的函数中,该函数将返回另一个 future 。
如果你在这里使用 thenComposeAsync,你只要求 supplyAsync 的执行必须提交给线程池,而不是直接在完成线程或“完成方法的任何其他调用者”中执行,例如直接在线程中调用 thenCompose
关于依赖的推理在这里没有意义。 “then”总是意味着依赖。如果在这里使用 thenComposeAsync ,则是强制将 action 提交到线程池,但是在 future 完成之前,仍然不会发生此提交。如果 future 异常完成,则根本不会提交。
那么,在这里使用 thenCompose 合理吗?是的,但不是因为给出的原因是报价。如上所述,使用非异步方法意味着放弃对正在执行的线程的控制,并且应该只在线程无关紧要时使用,尤其是对于简短的非阻塞操作。调用 supplyAsync 是一个廉价的 action,它会自己将实际的 action 提交给线程池,所以可以在任何空闲的线程中执行它。
然而,这是一个不必要的并发症。您可以使用
future.thenApplyAsync(quote -> Discount.applyDiscount(quote), executor)
这将完全相同,当 applyDiscount 完成时将 executor 提交给 future 并产生一个新的 future 代表结果。这里不需要组合使用 thenComposesupplyAsync
请注意,这个例子已经在 this Q&A 中讨论过,它还解决了 future 操作在多个 Stream 操作上的不必要隔离以及错误的序列图。

关于java - 作者使用 thenCompose 而不是 thenComposeAsync 的原因是否正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63217097/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com