“Java 8 in Action”对它提供的演示有误吗?

时间:2018-06-14 09:51:27

标签: java asynchronous java-8 completable-future

此代码引自Java 8 in Action,也在第11.4.3页中。

public Stream<CompletableFuture<String>> findPricesStream(String product) {
    return 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)));
}

在代码中,作者用数字括起来,表示applyDiscount()getPrice()在同一个线程中工作,我强烈怀疑:这里有两个不同的异步后缀,这意味着第二个调用应该在另一个线程中。

enter image description here

我使用以下代码在本地测试了它:

private static void testBasic() {
    out.println("*****************************************");
    out.println("********** TESTING thenCompose **********");
    CompletableFuture[] futures = IntStream.rangeClosed(0, LEN).boxed()
            .map(i -> CompletableFuture.supplyAsync(() -> runStage1(i), EXECUTOR_SERVICE))
            .map(future -> future.thenCompose(i -> CompletableFuture.supplyAsync(() -> runStage2(i), EXECUTOR_SERVICE)))
            .toArray(size -> new CompletableFuture[size]);
    CompletableFuture.allOf(futures).join();
}

输出进一步证明了我的想法,这是正确的吗?

*****************************************
********** TESTING thenCompose **********
Start: stage - 1 - value: 0 - thread name: pool-1-thread-1
Start: stage - 1 - value: 1 - thread name: pool-1-thread-2
Start: stage - 1 - value: 2 - thread name: pool-1-thread-3
Start: stage - 1 - value: 3 - thread name: pool-1-thread-4
Finish: stage - 1 - value: 3 - thread name: pool-1-thread-4 - time cost: 1520
Start: stage - 2 - value: 3 - thread name: pool-1-thread-5
Finish: stage - 1 - value: 0 - thread name: pool-1-thread-1 - time cost: 1736
Start: stage - 2 - value: 0 - thread name: pool-1-thread-6
Finish: stage - 1 - value: 2 - thread name: pool-1-thread-3 - time cost: 1761
Start: stage - 2 - value: 2 - thread name: pool-1-thread-7
Finish: stage - 2 - value: 2 - thread name: pool-1-thread-7 - time cost: 446
Finish: stage - 1 - value: 1 - thread name: pool-1-thread-2 - time cost: 2249
Start: stage - 2 - value: 1 - thread name: pool-1-thread-8
Finish: stage - 2 - value: 3 - thread name: pool-1-thread-5 - time cost: 828
Finish: stage - 2 - value: 0 - thread name: pool-1-thread-6 - time cost: 704
Finish: stage - 2 - value: 1 - thread name: pool-1-thread-8 - time cost: 401

Java 8 in Action错了吗?

谢谢你,@霍尔格。您现在可以清楚地了解异步非异步方法的执行线程。特别是在检查了specification之后,进一步证明了你的观点。

  

非异步方法的依赖完成提供的操作可能由完成当前CompletableFuture的线程执行,或由完成方法的任何其他调用方执行。< / p>

1 个答案:

答案 0 :(得分:7)

首先,由于不必要地拆分成多个Stream操作,该代码会分散正在发生的事情。

此外,做

没有任何意义
future.thenCompose(quote ->
    CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))

而不是

future.thenApplyAsync(quote -> Discount.applyDiscount(quote), executor)

因此,一个更简单的例子就是

public Stream<CompletableFuture<String>> findPricesStream(String product) {
    return shops.stream().map(
        shop -> CompletableFuture
            .supplyAsync(() -> shop.getPrice(product), executor)
            .thenApply(Quote::parse)
            .thenApplyAsync(quote -> Discount.applyDiscount(quote), executor));
}

但是,你是对的,没有保证getPriceapplyDiscount在同一个线程中运行 - 除非执行者是一个单线程执行者。

你可以将“executor thread”解释为“执行者线程之一”,但即使这样,图中也存在一个危险的错误点,即“new Quote(price)”,这显然实际上意味着“{{ 1}}”。该步骤属于右侧,因为未指定评估传递给Quote::parse的函数的实际线程。它可能是完成上一阶段后执行者的线程之一,但在调用thenApply时也可能是“你的主题”,例如如果异步操作设法在中间完成。

thenApply无法强制使用第一阶段为相关操作完成线程。

除非您使用简单的顺序代码,否则:

CompletableFuture

然后,右侧的线性线的图片将是正确的。