简体   繁体   English

如何使用 join 完成 CompletableFuture

[英]How to use join for complete CompletableFuture

Supposedly, I have the following method据说我有以下方法

public static Stream<CompletableFuture<String>> findPricesStream(String product) {}

This method will look for the cheapest price given a product and it returns a stream of CompletableFuture.此方法将查找给定product的最便宜价格,并返回 CompletableFuture 的 stream。

Now I want to react to the value in the stream as soon as it is available.现在我想在 stream 中的值可用时立即做出反应。 To do that, I adopt the method thenAccept and the implementation could be as following为此,我采用thenAccept方法,实现如下

    1. public static void reactToEarliestResultWithRaw() {
    2.     long start = System.nanoTime();
    3.     CompletableFuture[] priceFuture = findPricesStream(WHATEVER_PRODUCT_NAME)
    4.             .map(completableFuture -> completableFuture.thenAccept(
    5.                     s -> System.out.println(s + " (done in " + (System.nanoTime() - start) / 1_000_000 + " msecs)")))
    6.             .toArray(CompletableFuture[]::new);
    7.     CompletableFuture.allOf(priceFuture).join();
    8.     System.out.println("All shops have now responded in " + (System.nanoTime() - start) / 1_000_000 + " msecs)");
    9. }

With this implementation, I got the desired output通过这个实现,我得到了想要的 output

LetsSaveBig price is 151.227 (done in 5476 msecs)
BuyItAll price is 211.66 (done in 5747 msecs)
MyFavoriteShop price is 206.30200000000002 (done in 6968 msecs)
BestPrice price is 131.917 (done in 8110 msecs)
All shops have now responded in 8110 msecs)

Now I would like to take a further step to make the code more readable.现在我想进一步让代码更具可读性。 I chained another map that is responsible for joining all of CompletableFuture我链接了另一个负责加入所有 CompletableFuture 的 map

    1. public static void reactToEarliestResultWithoutRaw() {
    2.     long start = System.nanoTime();
    3.     List<Void> completeFutures = findPricesStream(WHATEVER_PRODUCT_NAME)
    4.             .map(completableFuture -> completableFuture.thenAccept(
    5.                     s -> System.out.println(s + " (done in " + (System.nanoTime() - start) / 1_000_000 + " msecs)")))
    6.             .map(CompletableFuture::join)
    7.             .toList();
    8.     int size = completeFutures.size();
    9.     if (isComplete(size)) {
    10.         System.out.println("All shops have now responded in " + (System.nanoTime() - start) / 1_000_000 + " msecs)");
    11. }
    12.
    13. private static boolean isComplete(int size) {
    14.     return size == shops.size();
    15. }    

I got the output我得到了 output

BestPrice price is 123.17400000000002 (done in 2060 msecs)
LetsSaveBig price is 109.67200000000001 (done in 6025 msecs)
MyFavoriteShop price is 131.21099999999998 (done in 13860 msecs)
BuyItAll price is 164.392 (done in 18434 msecs)
All shops have now responded in 18434 msecs)

The result makes me surprised!结果让我吃惊! I expect the elapsed time for both should be somehow the same , but they are a huge difference.我希望两者的经过时间应该是相同的,但它们是一个巨大的差异。

Do I misunderstand the way of using join here?我在这里误解了使用join的方式吗?

Reference参考

The implementation comes from the book Modern Java in Action: Lambdas, streams, functional and reactive programming 2nd Edition and I have modified it a bit for the experiment.该实现来自《 Modern Java in Action: Lambdas, streams, functional and reactive programming 2nd Edition 》一书,我为实验做了一些修改。

The "surprising" results are due to how findPricesStream is implemented: it returns shops.stream().map(shop -> CompletableFuture.supplyAsync(...) . The CompletableFuture is not constructed until a terminal operation is applied to the returned stream. This is done in your own method, after you call .toList() . “令人惊讶”的结果是由于findPricesStream是如何实现的:它返回shops.stream().map(shop -> CompletableFuture.supplyAsync(...) 。直到将终端操作应用于返回的 stream 才构造 CompletableFuture . 这是在你调用.toList()之后在你自己的方法中完成的。

The terminal operation toList() does this:终端操作toList()这样做:

  1. For the first shop , it constructs a CompletableFuture , which starts running.对于第一个shop ,它构造了一个CompletableFuture ,它开始运行。
  2. The CompletableFuture is joined, ie the main thread waits until it is finished. CompletableFuture 被加入,即主线程等待完成。
  3. Then the next CompletableFuture is constructed for the next shop, and so on.然后为下一个商店构造下一个 CompletableFuture,以此类推。

So the prices are calculated sequentially.所以价格是按顺序计算的。 To make the calculations run in parallel, create the list first (so that all futures are started) and then join them:要使计算并行运行,首先创建列表(以便启动所有期货),然后加入它们:

3.     List<Void> completeFutures = findPricesStream(WHATEVER_PRODUCT_NAME)
4.             .map(completableFuture -> completableFuture.thenAccept(
5.                     s -> System.out.println(s + " (done in " + (System.nanoTime() - start) / 1_000_000 + " msecs)")))
6.             .toList();
7.     completeFutures.foreach(CompletableFuture::join);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM