This code is a quoted from Java 8 in Action, which is also in the book 11.4.3.
public Stream> findPricesStream(String p
As a first note, that code is distracting from what’s happening due to the unnecessary splitting into multiple Stream operations.
Further, there is no sense in doing
future.thenCompose(quote ->
CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))
instead of
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));
}
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.
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.
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.