15. CompletableFuture异步编排

业务场景

查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。

假如商品详情页的每个查询,需要如下标注的时间才能完成

在这里插入图片描述

那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然这是不能接受的。
但是如果有多个线程同时完成这 6 步操作,那么只需要 1.5s 就可以完成响应。

简介

Future 是 Java 5 添加的类,可以获取异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel方法停止任务的执行。

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能让计算结果完成后及时通知监听者呢(观察者设计模式)

很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自己扩展了 Java 的 Future接口,提供了addListener等多个扩展方法;Google guava 也提供了通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。

作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?

最终在 jdk 1.8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性提供了函数式编程的能力可以通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法

CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或者轮询的方式获得结果,但是不推荐使用这种方式

在这里插入图片描述

创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作。

// 传入一个Runnable接口的实现类,无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable)
    
// 传入一个Runnable接口的实现类 | 自定义线程池,无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)  

// 传入一个要执行的异步任务,有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)  
    
// 传入一个要执行的异步任务 | 自定义线程池,有返回值    
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)  
  1. runAsync 的方法都是没有返回结果的,supplyAsync 的方法都是可以获取返回结果的
  2. 可以传入自定义的线程池,否则就用默认的线程池;

示例

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // runAsync:无返回值,未传入自定义线程池
//        CompletableFuture.runAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 5;
//            System.out.println("运行结果:" + i);
//        });

        // supplyAsync:有返回值,传入自定义线程池
        CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 5;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService);

        System.out.println("supAsync的返回结果:"+supAsync.get());

    }

方法完成后的感知

// 上一个异步任务执行完成之后的回调方法,里面可以获取到上一个异步任务的返回值 | 异常,使用的是同一个线程
// 虽然能得到异常信息,但是没法修改返回值
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)  
    
// 上一个异步任务完成之后,需要执行新的任务,这个任务还是以异步的方式交给线程池,让线程池自己找一个线程来处理
// 也可以获取到上一个异步任务的返回值 | 异常
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)  

//  上一个异步任务完成之后,指定自定义的线程池来执行新的任务
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)  

// 获取到上一个异步任务的异常,返回一个自定义的值
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)  

方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

示例

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

//        CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果:" + i);
//            return i;
//        }, executorService).whenComplete((res, thr) -> {
//            // 正常情况
//            System.out.println("supAsync的返回结果:" + res);
//            System.out.println("异常:" + thr);
//        });
        
        CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            // 异常情况
            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService).exceptionally(thr -> -1);

        System.out.println("supAsync的返回结果:" + supAsync.get());
    }

方法完成后的处理

和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值

public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)  

public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)  

public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)  

示例

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            // 模拟异常
//            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService).handle((res, thr) -> {
            if (res != null) {
                return res;
            }
            if (thr != null) {
                return -1;
            }
            return 0;
        });

        System.out.println("asyAsync的最终结果:" + supAsync.get());
    }

串行化方法

// 获取到上一个线程的返回值,再执行,需要有返回值
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)  
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)  
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)  

// 可以获取到上一个线程的返回值,再执行
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)  
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)  
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)  

// 不接收上一个线程的返回值,直接执行
public CompletableFuture<Void> thenRun(Runnable action)  
public CompletableFuture<Void> thenRunAsync(Runnable action)  
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)  

示例

    public static void main(String[] args) throws Exception {
        // thenRun
//        CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果:" + i);
//            return i;
//        }).thenRun(() -> System.out.println("任务2开始执行了"));
        
        // thenAccept
//        CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果:" + i);
//            return i;
//        }).thenAccept(res -> {
//            System.out.println("任务1的结果:" + res);
//        });

        // thenApply
        CompletableFuture<Integer> apply = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }).thenApply(res -> {
            System.out.println("任务1的结果:" + res);
            System.out.println("任务2开始...");
            return 1;
        });
        System.out.println("apply的最终结果:" + apply.get());
    }

两任务组合-都要完成

两个任务必须都完成,触发该任务

// 传入:第2个要执行的任务 | 可以获取到任务1和任务2的返回值,需要将当前的任务值返回
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, 
                                              BiFunction<? super T,? super U,? extends V> fn)  
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, 
                                            BiFunction<? super T,? super U,? extends V> fn)     
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, 
                                            BiFunction<? super T,? super U,? extends V> fn, Executor executor)
    

// 传入:第2个要执行的任务 | 可以获取到任务1和任务2的返回值,无返回值
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, 
                                                  BiConsumer<? super T,? super U> action)  
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, 
                                                              BiConsumer<? super T,? super U> action)  
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, 
                                                       BiConsumer<? super T,? super U> action, Executor executor)   
    

// 传入:第2个要执行的任务 | 2个任务都完成之后要做的任务,无返回值
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)  
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)  
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor)  

示例

    public static void main(String[] args) throws Exception {
        CompletableFuture<Integer> as1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始");
            int i = 10 / 5;
            return i;
        });

        CompletableFuture<Integer> as2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始");
            int i = 10 / 3;
            return i;
        });
        
        // runAfterBoth
//        as1.runAfterBoth(as2, () -> {
//            System.out.println("任务3开始");
//        });

        // thenAcceptBoth
//        as1.thenAcceptBoth(as2, (rs1, rs2) -> {
//            System.out.println("任务1结果:" + rs1);
//            System.out.println("任务2结果:" + rs2);
//        });

        // thenCombine
        CompletableFuture<Integer> as3 = as1.thenCombine(as2, (rs1, rs2) -> rs1 + rs2);
        System.out.println("任务1与任务2的结果之和:" + as3.get());
    }

两任务组合-一个完成

任务1组合任务2(other),只要有一个完成,就执行任务3(action/fn)

// 任务3可以获取到任务1/任务2的返回值,任务3需要有返回值
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn)  
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn)  
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor)  

// 任务3可以获取到任务1/任务2的返回值,任务3没有返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)  
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)  
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) 

// 任务3无返回值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)  
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)  
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)  

示例

public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(5);
        CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始...");
            int i = 10 / 9;
            System.out.println("任务1结束");
            return i;
        }, es);

        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始...");
            int i = 10 / 1;
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println("任务2结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return i;
        }, es);

        // runAfterEitherAsync
//        f1.runAfterEitherAsync(f2,() -> {
//            System.out.println("任务3执行");
//        },es);

        // acceptEither
//        f1.acceptEither(f2,res -> {
//            System.out.println("结果:"+res);
//        });

        // applyToEither
        CompletableFuture<Integer> f3 = f1.applyToEither(f2, res -> {
            System.out.println("任务3开始");
            int i = res * 10000;
            return i;
        });

        System.out.println("任务3最终结果:" + f3.get());
    }

多任务组合

// 所有任务都完成之后,才算结束
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)  
    
    
// 只要有一个任务完成,就算结束
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)  
    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(5);
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("开始查询基本信息");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "华为";
        }, es);

        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("开始查询图片信息");

            return "huawei.jpg";
        }, es);

        CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("开始查询销售属性");

            return "CPU品牌";
        }, es);
        
        // allOf
//        CompletableFuture<Void> allOf = CompletableFuture.allOf(f1, f2, f3);
//        System.out.println(f1.get() + "\t" + f2.get() + "\t" + f3.get());

        // anyOf
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(f1, f2, f3);
        System.out.println(anyOf.get());
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值