当我们需要异步处理任务时,最常用也是最简单的方式就是新开一个线程去做。而线程的创建和销毁是需要消耗 CPU 资源的,当异步任务越来越多时,如果一味的新开线程去处理,那么我们可能会无法控制 CPU 的资源。所以首先我们需要控制线程的开启数量。
类比数据库连接池,想一想之前使用 JDBC 访问数据库的时候是不是要先建立连接,也就是创建一个 Connection 对象。在 web 项目中,通常会有很多请求访问数据库,在使用框架 Spring+Mybatis 时,如果当前上下文没有事务的话那么每一个数据库操作方法都需要创建一个 Connection,这样频繁的创建 Connection 对象无疑是资源的浪费,也会拉低接口吞吐量。所以我们需要一个池子来维护数据库连接,也就是数据库连接池。同样我们也需要线程池。
JDK1.8 中提供的 CompletableFuture 提供了异步函数式编程。可以帮助我们简化异步编程的复杂性,通过回调的方式处理计算结果,并且提供了转换和组合的方法。
CompletableFuture是JDK1.8版本新引入的类,使用completionStage接口去支持完成时触发的函数和操作。
一个completetableFuture就代表了一个任务。他能用Future的方法。还能做一些之前说的executorService配合futures做不了的。
之前future需要等待isDone为true才能知道任务跑完了。或者就是用get方法调用的时候会出现阻塞。而使用completableFuture的使用就可以用then,when等等操作来防止以上的阻塞和轮询isDone的现象出现。
(1)创建 CompletableFuture 对象
提供了四个静态方法来创建:
- public static CompletableFuture
runAsync(Runnable runnable) - public static CompletableFuture
runAsync(Runnable runnable, Executor executor) - public static CompletableFuture supplyAsync(Supplier supplier)
- public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
默认使用ForkJoinPool.commonPool(), commonPool是一个会被很多任务共享的线程池,比如同一JVM上的所有CompletableFuture、并行Stream都将共享commonPool,commonPool设计时的目标场景是运行非阻塞的CPU密集型任务,为最大利用CPU,其线程数默认为CPU数量-1。
(2)阻塞获取
以下四个方法用于获取结果:
- public T get()
- public T get(long timeout, TimeUnit unit)
- public T getNow(T valueIfAbsent)
- public T join()
(3)计算完成时处理
这四个方法是计算阶段结束的时候触发
- public CompletableFuture
whenComplete(BiConsumer super T,? super Throwable> action) - public CompletableFuture
whenCompleteAsync(BiConsumer super T,? super Throwable> action) - public CompletableFuture
whenCompleteAsync(BiConsumer super T,? super Throwable> action, Executor executor) - public CompletableFuture
exceptionally(Function fn)
1、thenCompose
thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例
- public static void main(String[] args) {
- SmallTool.printTimeAndThread("小白进入餐厅");
- SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
-
- CompletableFuture
cf1 = CompletableFuture.supplyAsync(() -> { - SmallTool.printTimeAndThread("厨师炒菜");
- SmallTool.sleepMillis(200);
- return "番茄炒蛋";
- }).thenCompose(dish -> CompletableFuture.supplyAsync(() -> {
- SmallTool.printTimeAndThread("服务员打饭");
- SmallTool.sleepMillis(100);
- return dish + "米饭";
- }));
-
- SmallTool.printTimeAndThread("小白在打王者");
- SmallTool.printTimeAndThread(String.format("%s ,小白开吃 ", cf1.join()));
- }
结果输出:
2、thenCombine
会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值。
- public static void main(String[] args) {
- SmallTool.printTimeAndThread("小白进入餐厅");
- SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
- CompletableFuture
cf1 = CompletableFuture.supplyAsync(() -> { - SmallTool.printTimeAndThread("厨师炒菜");
- SmallTool.sleepMillis(200);
- return "番茄炒蛋";
- }).thenCombine(CompletableFuture.supplyAsync(() -> {
- SmallTool.printTimeAndThread("服务器蒸饭");
- SmallTool.sleepMillis(300);
- return "米饭";
- }), (dish, rice) -> {
- SmallTool.printTimeAndThread("服务器打饭");
- SmallTool.sleepMillis(100);
- return String.format("%s + %s 好了", dish, rice);
- });
-
- SmallTool.printTimeAndThread("小白在打王者");
- SmallTool.printTimeAndThread(String.format("%s ,小白开吃", cf1.join()));
- }
结果输出:

参考文档:
1、CompletableFuture 使用及应用场景_再见丶孙悟空的博客-CSDN博客_completablefuture使用场景
2、JDK1.8新特性CompletableFuture总结_finalheart的博客-CSDN博客_completablefuture
学习视频推荐:
1、CompletableFuture学习视频 :https://www.bilibili.com/video/BV1ar4y1x727?p=12&vd_source=13c57fbfc572ebd03c0f07686c93ce64
2、B站 CompletableFuture学习 :https://www.bilibili.com/video/BV1ui4y1T7xf/?spm_id_from=pageDriver&vd_source=7c9c5325e34e359ba5671d7a733fcfd3