CompletableFuture 是 JDK8 提出的一个支持非阻塞的多功能的 Future,提供了一种强大的异步编程模型,实现了 Future 接口;
在使用 Future 的过程中,大家会发现 Future 有两种方式返回结果的方式
- 阻塞方式get()
- 轮询方法isDone()
CompletableFuture 接口提供了非常多的方法用于编排异步任务,基本每个方法都有两套实现,Async 版本的函数与非 Async 版本的函数。
- 若方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
业务场景
-
查询商品详情页的逻辑比较复杂, 有些数据还需要远程调用, 必然需要花费更多的时间。
// 1.获取sku的基本信息 0.5s // 2.获取sku的图片信息 0.5s // 3.获取sku的促销信息 1s // 4.获取spu的所有销售属性 1s // 5.获取规格参数组及组下的规格参数 1.5s // 6. spu详情 1s
假如商品详情页的每个查询,需要按照标注的时间之和才能完成。即用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。
CompletableFuture 使用
-
Future 是 Java 5 添加的类,用来描述一个异步计算的结果。
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背, 轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能用
观察者设计模式
,当计算结果完成及时通知监听者呢? -
在 Java 8 中,新增加了一个包含 50 个方法左右的类:CompletableFuture,提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过 get 方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。
-
CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。
常用方法:
-
创建异步对象并执行(CompletableFuture)
CompletableFuture 提供了多种方法来创建异步对象,其中 runAsync 和 supplyAsync 是最常用的方法。
// 创建一个不返回结果的异步任务,使用默认线程池执行。 public static CompletableFuture<Void> runAsync(Runnable runnable) { return asyncRunStage(asyncPool, runnable); } // 创建一个不返回结果的异步任务,使用指定的线程池执行。 public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) { return asyncRunStage(screenExecutor(executor), runnable); } // 创建一个返回结果的异步任务,使用默认线程池执行。 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return asyncSupplyStage(asyncPool, supplier); } // 创建一个返回结果的异步任务,使用指定的线程池执行。 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) { return asyncSupplyStage(screenExecutor(executor), supplier); }
-
runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的。
-
可以传入自定义的线程池,否则就用默认的线程池。
示例:
// 创建一个线程池 ExecutorService executor = Executors.newFixedThreadPool(4); // 使用 supplyAsync 创建一个返回结果的异步任务 CompletableFuture<Integer> intFuture = CompletableFuture.supplyAsync(() -> { System.out.println("Calculating result..."); try { Thread.sleep(1000); // 模拟计算 } catch (InterruptedException e) { e.printStackTrace(); } return 42; // 返回计算结果 }, executor); // 确保主线程不会提前结束 intFuture.join(); // 关闭线程池 executor.shutdown();
-
-
thenAccept 方法,计算完成后再操作
用于在异步任务完成后再执行某个操作,且该操作不返回计算结果。该方法只会在异步任务成功完成时被调用,如果任务失败,则不会执行。
public CompletableFuture<V