携手创作,共同成长!这是我参与「掘金日新计划 · 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类,它提供了异常管理的机制,让你有机会抛出、管理异步任务执行种发生的异常
- 如果这些异步任务之间相互独立,或者他们之间的的某一些的结果是另一些的输入,你可以讲这些异步任务构造或合并成一个