为什么CompletableFuture的supplyAsync单独使用与搭配thenCompose返回类型不一致
类型嵌套的根本原因
CompletableFuture.supplyAsync() 的返回值类型由传入的 Supplier 函数的返回值决定,会自动给返回值套一层 CompletableFuture 包装:
- 你第二个版本中,
supplyAsync传入的 Supplier 最终返回的是CompletableFuture<List<Car>>类型,所以外层的supplyAsync自然就会把它再包一层,得到你遇到的CompletableFuture<CompletableFuture<List<Car>>>嵌套类型。 thenCompose的作用就是专门打平这种嵌套的CompletableFuture,它的定位和 Stream API 中的flatMap完全一致:如果前一步异步计算的返回值本身还是一个异步计算,thenCompose会自动把两层CompletableFuture拆成一层,所以第一个版本的返回类型是符合预期的。
符合你需求的实现方案
你要求先在主线程同步执行 fetchCars(),再异步执行多个 fetchRating(),完全不需要把 fetchCars 放到 supplyAsync 中,也不需要用到 thenCompose,直接按如下写法即可,返回类型天然就是 CompletableFuture<List<Car>>:
package comparison; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.function.Function; import static comparison.FakeDatabase.fetchCars; import static comparison.FakeDatabase.fetchRating; public class AsynchronousExample { public static void main(String[] args) { long start = System.currentTimeMillis(); // 主线程同步执行fetchCars,符合你的需求 List<Car> cars = fetchCars(); // 提前创建线程池复用,避免原有代码中每次循环都新建线程池的资源浪费问题 ExecutorService executor = Executors.newFixedThreadPool(Math.min(cars.size(), 7)); List<CompletableFuture<Car>> updatedCarFutures = cars.stream() .map(car -> CompletableFuture.supplyAsync(() -> { System.out.println("fetching id:\t" + car.id); car.rating = fetchRating(car.id); return car; }, executor)) .collect(Collectors.toList()); CompletableFuture<List<Car>> cfCars = CompletableFuture .allOf(updatedCarFutures.toArray(new CompletableFuture[0])) .thenApply(v -> updatedCarFutures.stream() .map(CompletableFuture::join) .collect(Collectors.toList())); cfCars.join().forEach(System.out::println); executor.shutdown(); long end = System.currentTimeMillis(); System.out.println("Took " + (end - start) + "ms"); } }
补充说明
如果你确实需要把 fetchCars 也放到异步线程执行,又不想显式写 thenCompose,可以在第二个版本的 supplyAsync 后加一行 .thenCompose(Function.identity()) 手动打平嵌套的Future,但这种写法不如直接用 thenCompose 清晰,更推荐使用第一个版本的写法。
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

