Java8新特性之CompletableFuture
CompletableFuture java8推出,java9增强,功能非常强大,可以编排异步任务,完成串行执行,并行执行,AND 汇聚关系,OR 汇聚关系。
任务之间的关系 并发、依赖、互斥
先弄个工具类,用来后面进行打印调试。
public class TestTool {
public static void sleepMillis(long millis) {
try {
Thread.sleep(millis); // 模拟线程操作时间
}catch (InterruptedException e){
e.printStackTrace();
}
}
public static void printTimeAndThread(String tag){
String result = new StringJoiner("\t|\t")
.add(String.valueOf(System.currentTimeMillis()))
.add(String.valueOf(Thread.currentThread().getId()))
.add(Thread.currentThread().getName())
.add(tag)
.toString();
System.out.println(result);
}
}
1、 runAsync 和 supplyAsync方法
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。
- runAsync方法不支持返回值。
- supplyAsync可以支持返回值。入参函数式接口可查看Java8新特性之函数式接口
下面例子completableFuture.join() 会等待 返回supplyAsync方法执行结束的结果 “菜品上齐”
public class completableFutureTest {
public static void main(String[] args){
TestTool.printTimeAndThread("小华进入餐厅");
TestTool.printTimeAndThread("小华点了 半只姜母鸭 + 一瓶可乐");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("小福贵制作姜母鸭");
TestTool.sleepMillis(200);
TestTool.printTimeAndThread("小福贵拿可乐");
TestTool.sleepMillis(100);
return "菜品上齐";
});
TestTool.printTimeAndThread("小华在刷微博");
TestTool.printTimeAndThread(String.format("%s,小华开吃", completableFuture.join()));
TestTool.printTimeAndThread("小华吃完");
}
}
2、thenCompose 前一个任务完成,有结果后,下一个任务才会触发
用来连接两个异步任务
入参dish 是"姜母鸭出锅" 而 出参需要一个CompletionStage的实现
public class completableFutureTest {
public static void main(String[] args){
TestTool.printTimeAndThread("小华进入餐厅");
TestTool.printTimeAndThread("小华点了 半只姜母鸭 + 一碗米饭");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("小福贵制作姜母鸭");
TestTool.sleepMillis(200);
return "姜母鸭出锅";
}).thenCompose(dish -> CompletableFuture.supplyAsync(() ->{
TestTool.printTimeAndThread("服务员拿米饭");
TestTool.sleepMillis(100);
return dish + ",米饭送到";
}));
TestTool.printTimeAndThread("小华在刷微博");
TestTool.printTimeAndThread(String.format("%s,小华开吃", completableFuture.join()));
TestTool.printTimeAndThread("小华吃完");
}
}
3、thenCombine 合并任务
thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
thenCombine 需要两个参数,一个CompletionStage的实现,一个BiFunction
BiFunction的作用是把两个值,转成一个值,可自己查看源码。
public class completableFutureTest {
public static void main(String[] args){
TestTool.printTimeAndThread("小华进入餐厅");
TestTool.printTimeAndThread("小华点了 半只姜母鸭 + 一碗米饭");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("小福贵制作姜母鸭");
TestTool.sleepMillis(200);
return "姜母鸭";
}).thenCombine(CompletableFuture.supplyAsync(() ->{
TestTool.printTimeAndThread("服务员蒸饭");
TestTool.sleepMillis(100);
return "米饭";
}),(dish, rice) ->{
TestTool.printTimeAndThread("服务员打饭");
TestTool.sleepMillis(100);
return String.format("%s + %s 好了", dish, rice);
});
TestTool.printTimeAndThread("小华在刷微博");
TestTool.printTimeAndThread(String.format("%s,小华开吃", completableFuture.join()));
TestTool.printTimeAndThread("小华吃完");
}
}
4、thenApply、thenApplyAsync
用来做任务的后置处理,有些场景可用thenCompose替代。
5、applyToEither
用来获取最先完成的任务。
public <U> CompletableFuture<U> applyToEither(
CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(null, other, fn);
}
上个任务和这个任务一起运行,哪个先运行完成,就把哪个任务结果交给Function
public class completableFutureTest {
public static void main(String[] args) {
TestTool.printTimeAndThread("小华到公交站");
TestTool.printTimeAndThread("等待 8路 或者 11路 公交车");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("8路公交车正在驶来");
TestTool.sleepMillis(1000);
return "8路到了";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("11路公交车正在驶来");
TestTool.sleepMillis(200);
return "11路到了";
}), firstComeBus -> firstComeBus);
TestTool.printTimeAndThread(String.format("%s,小华上车", completableFuture.join()));
}
}
6、exceptionally
用来处理异常。
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn) {
return uniExceptionallyStage(fn);
}
遇到异常时的处理动作
public class completableFutureTest {
public static void main(String[] args) {
TestTool.printTimeAndThread("小华到公交站");
TestTool.printTimeAndThread("等待 8路 或者 11路 公交车");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("8路公交车正在驶来");
TestTool.sleepMillis(1000);
return "8路到了";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
TestTool.printTimeAndThread("11路公交车正在驶来");
TestTool.sleepMillis(200);
return "11路到了";
}), firstComeBus -> {
TestTool.printTimeAndThread(firstComeBus);
if (firstComeBus.startsWith("11")){
throw new RuntimeException("追尾了");
}
return firstComeBus;
}).exceptionally(e ->{
TestTool.printTimeAndThread(e.getMessage());
TestTool.printTimeAndThread("小华直接叫滴滴");
return "滴滴到了";
});
TestTool.printTimeAndThread(String.format("%s,小华上车", completableFuture.join()));
}
}
7、allOf
归并结果
CompletableFuture<String> completableFuture1;
CompletableFuture<String> completableFuture2;
CompletableFuture<String> completableFuture3;
CompletableFuture.allOf(completableFuture1,completableFuture2,completableFuture3).join();