Java 8 in Action关于它提供的演示是错误的吗? [英] `Java 8 in Action` is wrong about the demo it provided?

查看:93
本文介绍了Java 8 in Action关于它提供的演示是错误的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码引自 Java 8 in Action ,也载于本书11.4.3.

This code is a quoted from Java 8 in Action, which is also in the book 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()在同一线程中工作,我对此深信不疑:有两个不同的 Async 后缀,这意味着第二个调用应该在另一个线程中.

Along the code, the writer enclose a figure as follows expressing that the applyDiscount() works in the same thread with getPrice(), which I strongly have a doubt: there are two different Async suffix here which means the second call should be in another thread.

我使用以下代码在本地对其进行了测试:

I tested it locally with the following code:

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();
}

输出进一步证明了我的想法,对吗?

The output further demonstrate my thought, is it correct?

*****************************************
********** 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对此有误吗?

谢谢你,@ Holger.您现在已经很清楚地了解异步非异步方法的执行线程.特别是在进一步检查其规范之后证明你的观点.

Thank you, @Holger. You make it crystal clear to me now about the executing thread for async and non-async methods. Especially after checking its specification further demonstrating your point.

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

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.

推荐答案

首先要注意的是,由于不必要的拆分成多个Stream操作,该代码分散了正在发生的事情.

As a first note, that code is distracting from what’s happening due to the unnecessary splitting into multiple Stream operations.

此外,这样做毫无意义

future.thenCompose(quote ->
    CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))

代替

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

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

So, a simpler example doing the same would be

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在同一线程中运行-除非执行程序是单线程执行程序.

However, you are right, there is no guaranty that getPrice and applyDiscount run in the same thread—unless the executor is a single threaded executor.

您可以将执行者线程"解释为执行者线程之一",但即使如此,在图中仍然存在危险的错误点,即"new Quote(price)",这显然意味着"Quote::parse". .该步骤不属于右侧,因为未指定评估传递给thenApply的函数的实际线程.在上一阶段完成后,它可能是执行者的线程之一,但在调用thenApply时,也可​​能是您的线程",例如如果异步操作设法在两者之间完成.

You may interpret "executor thread" as "one of the executor’s threads", but even then, there in a dangerously wrong point in the diagram, namely, "new Quote(price)", which apparently actually means "Quote::parse". That step does not belong to the right side, as the actual thread evaluating the function passed to thenApply is unspecified. It may be one of the executor’s threads upon completion of the previous stage, but it may also be "your thread" right when calling thenApply, e.g. if the asynchronous operation managed to complete in‑between.

CompletableFuture无法为相关操作强制使用第一阶段的完成线程.

The CompletableFuture offers no way to enforce the use of the first stage’s completing thread for the dependent actions.

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

Unless you use a simple sequential code instead, of course:

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

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

Then, the picture of a linear thread on the right hand side will be correct.

这篇关于Java 8 in Action关于它提供的演示是错误的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆