【Java】多线程异步编排CompletableFuture的Api方法学习

异步任务编排CompletableFuture

特别说明:JDK1.8+之后引入 CompletableFuture

背景

在一些需求中,我们常常要给前端返回一些复杂的视图数据,数据之间可能有关联,比如查询了A数据之后才可以根据A数据中的一些信息来查询B数据。

再比如 查询A数据与查询B数据一般没有相关性。假设查询A数据需要1秒,查询B数据需要1秒,那么以往的写法中,第一种情况返回给前端的时间总共花费2秒,

第二种情况返回给前端的时间也会是2秒。但这并不是我们愿意看到的,既然A数据跟B数据没有依赖性很强的情况下,应该可以并行查询进行封装,这样返回给前端的时间只需要花费1秒,这样大大增大了吞吐量,所以可以使用异步编排来解决该问题。

1.创建异步对象

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

//创建异步
public static CompletableFuture<Void> runAsync(Runnable runnable);
//创建异步,支持传入线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
//创建异步,并返回执行后的结果
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> sup);
//创建异步,支持传入线程池,并返回执行后的结果
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> sup, Executor executor)

1.runAsync方法都没有返回结果,supplyAsync方法都是有返回结果.

2.可以支持传入自己的线程池,或不传使用默认。

runAsync示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//第一种方式: runAsync
CompletableFuture.runAsync(() -> {
    System.out.println("执行runAsync方法....");
}, executorService);
System.out.println("执行main方法....");
//结果:
//执行main方法....
//执行runAsync方法....

supplyAsync示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5); 
//第二种方式:supplyAsync
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("执行supplyAsync方法...");
    return "hello,world";
}, executorService);
System.out.println("返回的结果是:"+future.get());
System.out.println("执行main方法....");
/**
结果:
执行supplyAsync方法...
返回的结果是:hello,world
执行main方法....
*/
2.完成回调和异常感知

某个异步任务完成之后执行某个任务


public CompletableFuture<T> whenComplete(
        BiConsumer<? super T, ? super Throwable> action);
//成功后回调异步执行,arg1:上次执行的结果,arg2:上次执行的异常
public CompletableFuture<T> whenCompleteAsync(
        BiConsumer<? super T, ? super Throwable> action);
//成功后回调异步执行,支持传入自己的线程池。arg1:上次执行的结果,arg2:上次执行的异常 
public CompletableFuture<T> whenCompleteAsync(
        BiConsumer<? super T, ? super Throwable> action, Executor executor);

whenComplete与whenCompleteAsync 区别在于 一个是上一个执行完的线程继续执行,一个是交由其他线程来执行。

whenComplete示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//完成回调  成功后执行某个事情
CompletableFuture.supplyAsync(()->{
    System.out.println("刷牙....");
    return "先刷牙";
},executorService).whenComplete((res,err)->{
    System.out.println("洗脸..."+"上一次结果:"+res+"--错误:"+err);
});
System.out.println("执行main方法....");
/*
刷牙....
洗脸...上一次结果:先刷牙--错误:null
执行main方法....
*/
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//完成回调  成功后执行某个事情
CompletableFuture.supplyAsync(()->{
    System.out.println("刷牙....");
    int i=1/0;
    return "先刷牙";
},executorService).whenComplete((res,err)->{
    System.out.println("洗脸..."+"上一次结果:"+res+"--错误:"+err);
});
System.out.println("执行main方法....");
/*
执行main方法....
刷牙....
洗脸...上一次结果:null--错误:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
*/

虽然能得到异常信息,但是没办法修改返回数据。使用方法exceptionally可以得到解决。

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//完成回调  成功后执行某个事情
CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙....");
    int i = 1 / 0;
    return "先刷牙";
}, executorService).whenComplete((res, err) -> {
    System.out.println("洗脸..." + "上一次结果:" + res + "--错误:" + err);
}).exceptionally(e->{
    return "异常了";
});
System.out.println("执行结果:"+exceptionally.get());
System.out.println("执行main方法....");
/*
刷牙....
洗脸...上一次结果:null--错误:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
执行结果:异常了
执行main方法....
*/
3.使用handle方法

以上的whenComplete 以及exceptionally 都可以用handler代替

//由上一个执行的线程来继续执行  T:上一次执行的结果  U:返回结果
public <U> CompletableFuture<U> handle(
        BiFunction<? super T, Throwable, ? extends U> fn);
//由其他线程来执行 T:上一次执行的结果  U:返回结果
public <U> CompletableFuture<U> handleAsync(
        BiFunction<? super T, Throwable, ? extends U> fn);
//由其他线程来执行,支持传入线程池。 T:上一次执行的结果  U:返回结果
public <U> CompletableFuture<U> handleAsync(
        BiFunction<? super T, Throwable, ? extends U> fn, Executor executor);

handle示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<String> handle = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    int i = 1 / 0;
    return "正常刷完牙了";
}).handleAsync((res, err) -> {
    if (res != null) {
        return res;
    }
    if (err != null) {
        return "刷牙的时候有问题";
    }
    return "返回值是null,但也没抛异常";
},executorService);
System.out.println("执行结果:"+handle.get());
System.out.println("执行main方法....");
/*
刷牙...
执行结果:刷牙的时候有问题
执行main方法....
*/
4.线程串行化方法
//上一个任务执行后,接下来执行的任务  由上一次任务执行后的线程继续执行.
public CompletableFuture<Void> thenRun(Runnable action);
//上一个任务执行后,接下来执行的任务  由不同的线程继续执行.
public CompletableFuture<Void> thenRunAsync(Runnable action);
//上一个任务执行后,接下来执行的任务  由线程池继续执行.
public CompletableFuture<Void> thenRunAsync(Runnable action,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 <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);

thenRun都是无返回值的,不需要上一次任务执行的结果。

thenAccept 都是可以接收到上一次任务执行的结果,用于下一次执行使用。

thenApply 接收上一次执行的结果,并有返回值。

thenRun示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return null;
}, executorService).thenRun(() -> {
    System.out.println("洗脸");
});
//阻塞等待方法
future.get();
/*
刷牙...
洗脸
*/

thenAccept示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return "我刷完了";
}, executorService).thenAcceptAsync(result -> {
    System.out.println("接收结果:"+result);
    System.out.println("洗脸");
});
//阻塞等待方法
future.get();
/*
刷牙...
接收结果:我刷完了
洗脸
*/

thenApply示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return "我刷完了";
}, executorService).thenApplyAsync(result -> {
    System.out.println("接受结果:"+result);
    System.out.println("洗脸");
    return "我洗完了";
});
//阻塞等待方法
String s = future.get();
System.out.println("最终结果:"+s);
/*
刷牙...
接受结果:我刷完了
洗脸
最终结果:我洗完了
*/
5.两任务组合-都要完成

当两个任务都执行完毕之后,再执行当前任务。

//两个任务都完成,没有接收他们的结果,没有返回值
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 <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);
//两个任务都完成 接收他们的结果并执行新任务 并返回当前任务的返回值。
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);

runAfterBoth示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return "我刷完了";
}, executorService);

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("洗脸...");
    return "我洗完了";
}, executorService);
future1.runAfterBothAsync(future2, () -> {
    System.out.println("吃饭");
}, executorService);
/*
刷牙...
洗脸...
吃饭
*/

thenAcceptBoth示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return "我刷完了";
}, executorService);

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("洗脸...");
    return "我洗完了";
}, executorService);
future1.thenAcceptBothAsync(future2, (f1,f2) -> {
    System.out.println("接受f1结果:"+f1);
    System.out.println("接受f2结果:"+f2);
    System.out.println("吃饭");
}, executorService);
/*
刷牙...
洗脸...
接受f1结果:我刷完了
接受f2结果:我洗完了
吃饭
*/

thenCombine示例

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("刷牙...");
    return "我刷完了";
}, executorService);

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("洗脸...");
    return "我洗完了";
}, executorService);
CompletableFuture<String> future3 = future1.thenCombineAsync(future2, (f1, f2) -> {
    System.out.println("接受f1结果:" + f1);
    System.out.println("接受f2结果:" + f2);
    System.out.println("吃饭");
    return "吃完了";
}, executorService);
String s = future3.get();
System.out.println("最终结果:"+s);
/*
刷牙...
洗脸...
接受f1结果:我刷完了
接受f2结果:我洗完了
吃饭
最终结果:吃完了
*/
6.两任务组合-一个完成

只要两个任务完成一个,就会触发第三个任务的执行。

//接收某个任务执行完成的结果,有返回值.
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);
//接收某个任务执行完成的结果,无返回值.
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);
//不接收某个任务执行完成的结果,也无返回值.
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);
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰肥啊

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值