Completablefuture 详解之后续处理

563 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

上一章节我们对Completablefuture的操作有了一定了解Completablefuture详解,这一节我们继续学习Completablefuture的后续处理。

一、Completablefuture后续操作方法介绍

1、handle

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)

handle方法集和前面说讲的complete方法集没有区别,同样有两个参数一个返回结果和可抛出异常,区别就在于返回值,虽然同样返回CompletableFuture类型,但是里面的参数类型handle方法是可以自定义的。

// 开启一个异步方法
CompletableFuture<List> future = CompletableFuture.supplyAsync(() -> {
    List<String> list = new ArrayList<>();
    list.add("语文");
    list.add("数学");
    // 获取得到今天的所有课程
    return list;
});
// 使用handle()方法接收list数据和error异常
CompletableFuture<Integer> future2 = future.handle((list,error)-> {
    // 如果报错,就打印出异常
    error.printStackTrace();
    // 如果不报错,返回一个包含Integer的全新的CompletableFuture
    return list.size();
     // 注意这里的两个CompletableFuture包含的返回类型不同
});

2、异步回调功能:thenApply

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)

thenApply第一个任务结束,可能还有第二、第三个任务,且后面一个任务,需要用到前面任务的返回值;计算之后的后续操作,唯一的不同是,handle方法会给出异常,可以让用户自己在内部处理,而apply方法只有一个返回结果,如果异常,会被直接抛出,交给上一层处理。 如果不想每个链式调用都处理异常,那么就使用apply。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    int result = 100;
    System.out.println("一阶段:" + result);
    return result;
}).thenApply(number -> {
    int result = number * 3;
    System.out.println("二阶段:" + result);
    return result;
}).thenApply(number -> {
    int result = number * 3;
    System.out.println("三阶段:" + result);
    return result;
});

System.out.println("最终结果:" + future.get());

3、对结果进行消耗:thenAccept

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)

accept()只做最终结果的消费,此时返回的CompletableFuture是只消费,无返回,有点像流式编程的终端操作

// 实例化一个CompletableFuture,返回值是Integer
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // 返回null
    return null;
});

CompletableFuture<String> exceptionally = future.thenApply(result -> {
    // 制造一个空指针异常NPE
    int i = result;
    return i;
}).thenApply(result -> {
    // 这里不会执行,因为上面出现了异常
    String words = "现在是" + result + "点钟";
    return words;
}).exceptionally(error -> {
    // 我们选择在这里打印出异常
    error.printStackTrace();
    // 并且当异常发生的时候,我们返回一个默认的文字
    return "出错啊~";
});

exceptionally.thenAccept(System.out::println);

4、 上一步结果与下一步操作无关系:thenRun

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

在执行CompletableFuture后,如果得到的结果对下一步没有影响,也就是说下一步的操作并不关心上一步的结果,最终也不返回值,可以使用thenRun,参数传递一个Runnable。

thenRun也是消费结果,thenRun跟thenAccept的区别是,它不仅不产生新的值,还不消费上个任务的值,只是自己做一个业务处理。

5、两个完全不相干的对象的结果整合起来,两项任务可以同时执行:thenCombine

public <U,V> CompletableFuture<V> thenCombine
public <U,V> CompletableFuture<V> thenCombineAsync
public <U,V> CompletableFuture<V> thenCombineAsync

允许前后连接的两个任务可以并行执行,后置任务不需要等待前置任务执行完成,最后当两个任务均完成时,再将其结果同时传递给下游处理任务,从而得到最终结果。

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 2");
    return 10;
});
CompletableFuture<Integer> future3 = future1.thenCombine(future2, (r1, r2)->r1 + r2);
System.out.println("result: " + future3.join());

6、对2个cf的结果进行组合:thenCompose

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;

thenCompose方法可以将2个独立的任务进行流水线操作。将当前cf的计算结果作为参数传递给后面的cf。

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture<Integer> future2 = future1.thenCompose((r)->CompletableFuture.supplyAsync(()->r+10));
System.out.println(future2.join());

6、2个cf都执行完后执行操作:runAfterBoth

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) 

两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。

结果组合方法还有:

  • thenCombine -合并两个线程任务的结果,并进一步处理。
  • applyToEither-两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。
  • acceptEither-两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。
  • runAfterEither-两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。
  • runAfterBoth-两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。
  • anyOf-anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture。
  • allOf-allOf方法用来实现多 CompletableFuture 的同时返回。

7、获取所有完成结果——allOf

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

allOf方法,当所有给定的任务完成后,返回一个全新的已完成CompletableFuture

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        //使用sleep()模拟耗时操作
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 1;
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
    return 2;
});
CompletableFuture.allOf(future1, future1);
// 输出3
System.out.println(future1.join()+future2.join());

8、获取率先完成的任务结果——anyOf

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

仅等待Future集合种最快结束的任务完成并返回它的结果。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            throw new NullPointerException();
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                // 睡眠3s模拟延时
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 1;
        });
        CompletableFuture<Object> anyOf = CompletableFuture
                .anyOf(future, future2)
                .exceptionally(error -> {
                    error.printStackTrace();
                    return 2;
                });
        System.out.println(anyOf.join());

9、异常处理

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)

exceptionally()  可以帮我们捕捉到所有中间过程的异常,方法会给我们一个异常作为参数,我们可以处理这个异常,同时返回一个默认值,跟服务降级 有点像。

        // 实例化一个CompletableFuture,返回值是Integer
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 返回null
            return null;
        });

        CompletableFuture<String> exceptionally = future.thenApply(result -> {
            // 制造一个空指针异常NPE
            int i = result;
            return i;
        }).thenApply(result -> {
            // 这里不会执行,因为上面出现了异常
            String words = "现在是" + result + "点钟";
            return words;
        }).exceptionally(error -> {
            // 我们选择在这里打印出异常
            error.printStackTrace();
            // 并且当异常发生的时候,我们返回一个默认的文字
            return "出错啊~";
        });

        exceptionally.thenAccept(System.out::println);

    }

10、多个方法组合使用

public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> 1)
                .whenComplete((result, error) -> {
                    System.out.println(result);
                    error.printStackTrace();
                })
                .handle((result, error) -> {
                    error.printStackTrace();
                    return error;
                })
                .thenApply(Object::toString)
                .thenApply(Integer::valueOf)
                .thenAccept((param) -> System.out.println("done"));
    }

二、使用CompletableFuture场景

  • 执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度
  • 使用CompletableFuture类,它提供了异常管理的机制,让你有机会抛出、管理异步任务执行种发生的异常
  • 如果这些异步任务之间相互独立,或者他们之间的的某一些的结果是另一些的输入,你可以讲这些异步任务构造或合并成一个