作者:Mars酱
声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。
转载:欢迎转载,转载前先请联系我!
前言
继续讲,前面讲了Future实现异步,优点缺点也都有,这里讲使用CompletableFuture机制,目前为止,应该说JDK原生提供的异步方式的最优方案就是CompletableFuture了,已知的开源框架Dubbo中的RPC协议实现、常用的注册配置中心Nacos中CP协议的具体实现JRaft模块,也使用CompletableFuture。好了,名气大,不多说了。
CompletableFuture实现异步
我们还是用 Java | 一分钟掌握异步编程 | 4 - Future异步 - 掘金 (juejin.cn) 中3号桌点餐的例子,3号桌点了很多餐:红烧鱼、油焖虾,中途厨子还要上厕所,这个过程,我们用 CompletableFuture 来实现,我们把两道菜拆分成两个任务,一个内急的好厨子一定是可以双飞两道菜的,先飞一道鱼:
/**
* @author mars酱
*/
public class BraisedFish {
public BraisedFish() {
System.out.println("1. 抓鱼");
}
public void kill() {
System.out.println("2. 杀鱼");
}
public void cooking() {
System.out.println("3. 红烧鱼制作中...");
}
}
再来一盘虾:
/**
* @author mars酱
*/
public class BraisedPrawns {
public BraisedPrawns() {
System.out.println("1. 捉虾");
}
public void kill() {
System.out.println("2. 杀虾");
}
public void cooking() {
System.out.println("3. 油焖大虾制作中...");
}
}
厨师的动作:
/**
* @author mars酱
*/
public class Chef {
public Chef() {
System.out.println("1. 米其林大厨师");
}
public void toilet() {
System.out.println("厨师正在拉,请稍后...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
做鱼的任务顺序必须是捉鱼 -> 杀鱼 -> 红烧;做虾的顺序必须是先捉后杀再油焖。最后,整体流程是先做鱼,再做虾,厨子上厕所,菜好了等着厨子上完厕所洗完手,再撒上葱花:
/**
* @author mars酱
*/
public class NoodlesRestaurant3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 厨师、鱼、虾都准备到位
Chef chef = new Chef();
BraisedFish braisedFish = new BraisedFish();
BraisedPrawns braisedPrawns = new BraisedPrawns();
System.out.println("---- 准备工作完成 ----");
// 2. 做鱼
CompletableFuture<String> killFishTask = CompletableFuture.supplyAsync(() -> {
braisedFish.kill();
return ">> 鱼杀好了,准备红烧";
});
CompletableFuture<String> cookingFishTask = killFishTask.thenApply((result) -> {
System.out.println(result);
braisedFish.cooking();
return ">> 红烧鱼作品已完成";
});
// 3. 做虾
CompletableFuture<String> killPrawnsTask = CompletableFuture.supplyAsync(() -> {
braisedPrawns.kill();
return ">> 虾杀好了,准备油焖";
});
CompletableFuture<String> cookingPrawnsTask = killPrawnsTask.thenApply((result) -> {
System.out.println(result);
braisedPrawns.cooking();
return ">> 油焖虾作品已完成";
});
// 4. 厨师拉粑粑
CompletableFuture<String> chefToilet = CompletableFuture.supplyAsync(() -> {
chef.toilet();
return ">> 厨师的粑粑拉完啦!";
});
CompletableFuture<String> chefWashHands = chefToilet.thenApply((result) -> {
System.out.println(result);
return ">> 厨师洗手!";
});
// 5. 菜品都完成了
CompletableFuture<String> foodCompleted = cookingFishTask.thenCombineAsync(cookingPrawnsTask, (w, s) -> w + " 和 " + s + ", 等待厨师撒葱花!");
// 6. 等待厨师上完厕所撒葱花
CompletableFuture<String> serving = chefWashHands.thenApply((result) -> {
String join = foodCompleted.join();
System.out.println(join);
System.out.println(result);
System.out.println(">> 厨师用擦过花的手撒葱花,干净又卫生!");
return ">> 上菜吧~";
});
System.out.println(serving.get());
}
}
运行一下下:
很好,厨师果然正在拉,继续等待…
方法简介
以上用到了一些CompletableFutre的api,这里把常用的api简单介绍一下:
一般
supplyAsync
:执行CompletableFuture任务,支持返回值
runAsync
:执行CompletableFuture任务,没有返回值。
异步回调
thenRun/thenRunAsync
:做完第一个任务后,再做第二个任务。
thenAccept/thenAcceptAsync
:第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,但是回调方法是没有返回值的
thenApply
:第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。
exceptionally
:某个任务执行异常时,执行的回调方法;并且有抛出异常作为参数,传递到回调方法。
whenComplete
:某个任务执行完成后,执行的回调方法,无返回值;并且whenComplete方法返回的CompletableFuture的result是上个任务的结果。
handle
:某个任务执行完成后,执行回调方法,并且是有返回值的;并且handle方法返回的CompletableFuture的result是回调方法执行的结果。
thenCombineAsync
:两个异步任务t1、t2是并行执行,彼此无先后依赖顺序,thenCombineAsync适合将两个并行执行的异步任务的结果合并返回成一个新的future。
都完成
thenCombine
/ thenAcceptBoth
/ runAfterBoth
都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务。
区别在于:
- thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
- thenAcceptBoth: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
- runAfterBoth 不会把执行结果当做方法入参,且没有返回值。
其中一个完成
applyToEither
/ acceptEither
/ runAfterEither
都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。区别在于:
- applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
- acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
- runAfterEither: 不会把执行结果当做方法入参,且没有返回值。
所有都匹配
所有任务都执行完成后,才执行 allOf返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常
任意一个匹配
任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture,执行get方法,会抛出异常
thenCompose
thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例
- 如果该CompletableFuture实例的result不为null,则返回一个基于该result新的CompletableFuture实例;
- 如果该CompletableFuture实例为null,然后就执行这个新任务
结尾
CompletableFuture 优秀的地方之一就是它的任务的编排。比如:先完成a,再完成b;a和b同时完成再做c,满足各种各样的业务组合方式。那么,好好利用编排功能完成工作中的难点吧。