CompletableFuture 异步编排
一、 需求:当异步任务产生关系与顺序后,怎么办呢?
1).如三个线程ABC,A需要C执行结束才能执行,B随便执行,怎么处理呢?
2).解决方法:CompletableFuture 异步编排
二、业务场景
1.1).场景介绍
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间
1.2).业务关系
4、5、6.需要1中的spuId.。复杂,相互关联
三、CompletableFuture使用
1.创建异步对象
1).四个静态方法
CompletableFuture 提供了四个静态方法来创建一个异步操作。
public static CompletableFuture<Void> runAsync(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.1).CompletableFuture 的启动与执行–runAsync(返无回值)、supplyAsync(有返回值)
runAsync
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start");
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("当前线程id " + Thread.currentThread().getId());
int i = 12 / 2;
System.out.println("当前线程处理结果 " + i);
}, executor);
System.out.println("main...end");
}
supplyAsync
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程id " + Thread.currentThread().getId());
int i = 12 / 2;
System.out.println("当前线程处理结果 " + i);
return i;
}, executor);
Integer integer = future.get();
System.out.println("future: "+integer);
System.out.println("main...end");
}
2.计算完成时回调方法
2.1).完成回调与异常感知whenComplete、exceptionally(方法完成后的感知)
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future= CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程id " + Thread.currentThread().getId());
int i = 12 / 0;
System.out.println("当前线程处理结果 " + i);
return i;
}, executor).whenComplete((res, exception) -> {
System.out.println("异步任务完成了。。。结果是: " + res + ",异常时:" + exception);
}).exceptionally(throwable -> {
return 10;
});
Integer integer = future.get();
System.out.println("main...end"+integer);
}
总结:
1).whenComplete、 exceptionally
whenComplete:可以得到异常信息,没法修改返回数据
exceptionally:可以感知异常,同时返回默认 (可以修改返回信息)
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。
2).whenComplete 和 whenCompleteAsync 的区别:(是否使用统一线程执行任务)
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池
来进行执行。
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程
执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
3.handle 方法–处理异常(方法完成后的处理)
//使用handle处理异常(方法完成后的处理)
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程id " + Thread.currentThread().getId());
int i = 12 / 4;
System.out.println("当前线程处理结果 " + i);
return i;
}, executor).whenComplete((res, exception) -> {
System.out.println("异步任务完成了。。。结果是: " + res + ",异常时:" + exception);
}).handle((res,throwable)->{
if (res!=null){
return res*2;
}
if(throwable!=null) {
System.out.println("有异常");
return 0;
}
return 0;
});
Integer integer = future.get();
System.out.println("main...end: "+integer);
}
和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。
**handle与handleSync区别**:是否处理结果业务 使用相同线程
4.线程串行化方法-thenApply 、thenAccept 、thenRun
原理;一个任务结束,继续执行其他任务,组成连串任务
4.1)thenRun:不能获取上一个任务的执行结果
4.2)thenAccept:可以接受上一步执行结果,但没返回值
4.3).thenApply:可以接受上一步执行结果,有返回值
总结:添加Async就是继续执行任务就不是同一线程了
thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前
任务的返回值。
thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun 方法:只要上面的任务执行完成,就开始执行 thenRun,只是处理完任务后,执行thenRun 的后续操作
注意:带有 Async 默认是异步执行的。同之前whenComplete、handle。
以上都要前置任务成功完成。
Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型
5.两任务组合 - 都要完成thenCombine、thenAcceptBoth、runAfterBoth
原理:两任务组合 - 都要完成,才执行任务3。
thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有
返回值。
runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,
处理该任务。
1).创建2个任务(线程)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1的线程id: " + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("任务1结束: " + i);
return i;
}, executor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2 线程id: " + Thread.currentThread().getId());
System.out.println("任务2结束");
return "hello";
}, executor);
2).组合他们(3中方式)
2.1). runAfterBothAsync:不可获取前2个任务结果,无返回值
future1.runAfterBothAsync(future2,()->{
System.out.println("任务3开始");
},executor);
System.out.println("main...end, ");
2.2).thenAcceptBothAsync:可获取前2个任务结果,但无返回值
future1.thenAcceptBothAsync(future2, (f1, f2) -> {
System.out.println("任务3开始, f1= "+f1+ ",f2="+f2);
}, executor);
System.out.println("main...end, ");
2.3).thenCombineAsync:可获取前2个任务结果,有返回值
CompletableFuture<String> future = future1.thenCombineAsync(future2, (f1, f2) -> {
System.out.println("任务3开始, f1= " + f1 + ",f2=" + f2);
return f1 + ":" + f2;
}, executor);
System.out.println("结果:"+future.get());
System.out.println("main...end, ");
6.两任务组合 – 一个完成,才执行任务3
原理:当两个任务中,任意一个 future 任务完成的时候,执行任务。
applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返
回值。
1).创建2个任务future
2).组合他们(三种方式)
2.1).runAfterEitherAsync-不感知结果,无返回值
future1.runAfterEitherAsync(future2,()->{
System.out.println("任务3");
},executor);
System.out.println("main...end ");
2.2).acceptEitherAsync-感知上一个任务结果,无返回值
future1.acceptEitherAsync(future2,(res)->{
System.out.println("任务3,"+res);
},executor);
System.out.println("main...end ");
2.3).applyToEitherAsync-感知上一个任务结果, 有返回值
CompletableFuture<String> future = future1.applyToEitherAsync(future2, (res) -> {
System.out.println("任务3," + res);
return res + ":hello";
}, executor);
System.out.println("main...end, "+future.get());
7、多任务组合
1).创建三个future
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品图片信息");
return "hello.jpg";
}, executor);
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品属性信息");
return "黑色+256G";
}, executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("查询商品绝介绍");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "华为P30";
}, executor);
2).使用
2.1)allOf:等待所有任务完成
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
allOf.get();//等待所有结果都完成(必须加,否则就不能实现等待所有任务完成)
System.out.println("main...end");
2.2)anyOf:只要有一个任务完成
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
anyOf.get();
System.out.println("main...end"+anyOf.get());