这个问题不同于这个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());
}
Run Code Online (Sandbox Code Playgroud)
一切正常,我可以理解这段代码,但这是作者为什么没有thenComposeAsync在第 408 页使用的原因,我无法理解:
通常,名称中没有 Async 后缀的方法在与前一个任务相同的线程中执行其任务,而以 Async 终止的方法总是将后续任务提交到线程池,因此每个任务都可以由不同的线程处理线。在这种情况下,第二个 CompletableFuture 的结果取决于第一个,因此无论您使用此方法的一个或另一个变体组合两个 CompletableFuture,对最终结果或其粗略计时都没有影响
根据我对thenCompose( 和thenComposeAsync) 签名的理解,如下所示:
public <U> …Run Code Online (Sandbox Code Playgroud) 我开始熟悉 JavaCompletableFuture组合,使用过 JavaScript 承诺。基本上,组合只是在指定的执行器上安排了链式命令。但是我不确定在执行组合时哪个线程正在运行。
假设我有两个执行者,executor1并且executor2;为简单起见,假设它们是单独的线程池。我安排了一个CompletableFuture(使用非常松散的描述):
CompletableFuture<Foo> futureFoo = CompletableFuture.supplyAsync(this::getFoo, executor1);
Run Code Online (Sandbox Code Playgroud)
然后,当做到这一点我转换Foo到Bar使用第二执行人:
CompletableFuture<Bar> futureBar .thenApplyAsync(this::fooToBar, executor2);
Run Code Online (Sandbox Code Playgroud)
我知道getFoo()将从executor1线程池中的线程调用。我知道fooToBar()将从executor2线程池中的线程调用。
但是什么线程用于实际的组合,即在getFoo()完成和futureFoo()完成之后;但之前的fooToBar()命令被安排在executor2?换句话说,哪个线程实际运行代码以在第二个执行器上调度第二个命令?
调度是否作为executor1调用的同一线程的一部分执行getFoo()?如果是这样,这个可完成的未来组合是否等同于我fooToBar()在executor1任务的第一个命令中自己手动安排?
这是我面临的问题的简短代码版本:
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
/*
try {
Thread.sleep(2000);
} catch (InterruptedException ignored) {}
*/
//System.out.println("supplyAsync: " + Thread.currentThread().getName());
return 1;
})
.thenApply(i -> {
System.out.println("apply: " + Thread.currentThread().getName());
return i + 1;
})
.thenAccept((i) -> {
System.out.println("accept: " + Thread.currentThread().getName());
System.out.println("result: " + i);
}).join();
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的输出:
apply: main
accept: main
result: 2
Run Code Online (Sandbox Code Playgroud)
看到main那里我很惊讶!Thread.sleep()我预计当我取消注释该调用或什至取消注释单个语句时会发生类似的情况sysout:
supplyAsync: ForkJoinPool.commonPool-worker-1
apply: ForkJoinPool.commonPool-worker-1
accept: ForkJoinPool.commonPool-worker-1
result: 2
Run Code Online (Sandbox Code Playgroud)
我理解thenApplyAsync()将确保它不会在main线程上运行,但我想避免将供应商从运行的线程返回的数据传递supplyAsync …
java multithreading asynchronous completable-future completion-stage