CompletableFuture是用来进行异步编程的,核心代码都放到了另一个线程计算,那么计算的线程如果发生了异常,改如何处理呢?
我们先看demo,demo的功能和CompletableFuture第一节是一样的,查询多个商品的价格,但是其中某个商品的价格获取非常耗时,所以采用了CompletableFuture异步计算,看代码:
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* 使用CompletableFuture异步编程
* @author www.itzhimei.com
*/
public class FutureTest_2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//进行异步价格计算
Future<Integer> priceA = getPriceAsync();
//执行其他业务逻辑
printOtherPrice();
//获取价格计算结果
Integer price = priceA.get();
System.out.println("异步获取价格" + price);
}
public static void printOtherPrice() {
for(int i=0; i<10; i++) {
System.out.println("获取其他商品价格:" + new Random().nextInt());
}
}
public static Future<Integer> getPriceAsync() {
CompletableFuture<Integer> future = new CompletableFuture<>();
new Thread(()->{
try {
//模拟计算复杂逻辑
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete(new Random().nextInt());
}).start();
return future;
}
}
/* 输出
获取其他商品价格:450142711
获取其他商品价格:-1207991723
获取其他商品价格:-264920736
获取其他商品价格:1122276343
获取其他商品价格:-652082111
获取其他商品价格:1312307709
获取其他商品价格:-417893410
获取其他商品价格:1957242712
获取其他商品价格:1257272721
获取其他商品价格:100803481
异步获取价格-1514711276
*/
1、一种方法是在获取异步结果的时候做调整
CompletableFuture的get方法和Future的get方法一样,都是获取异步结果,如果没有结果(发生异常)或者结果计算特别慢,则程序一直阻塞在此,那么此时可以使用另一个get方法。
get(long timeout, TimeUnit unit),这个方法可以设置等待时间,超过等待时间则继续执行后续代码。
2、第二种是进行异常返回
如果调用的异步线程中发生异常,此时发起调用的线程是不知道具体错误的,CompletableFuture提供了可以返回异步线程内的错误的方法futurePrice.completeExceptionally(ex),我们demo的代码可以做一些调整,如下:
public static Future<Integer> getPriceAsync() {
CompletableFuture<Integer> future = new CompletableFuture<>();
new Thread(()->{
try {
//模拟计算复杂逻辑
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
int a = 10;
int b = 0;
future.complete(a/b);//模拟一个异常
//future.complete(new Random().nextInt());
} catch (Exception ex) {
future.completeExceptionally(ex);
}
}).start();
return future;
}
/* 输出
获取其他商品价格:5158158
获取其他商品价格:-787358034
获取其他商品价格:1026159408
获取其他商品价格:1082639987
获取其他商品价格:227190370
获取其他商品价格:-1008236480
获取其他商品价格:-267365833
获取其他商品价格:-346777568
获取其他商品价格:1282838883
获取其他商品价格:-2081155736
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2237)
at com.itzhimei.FutureTest_3.main(FutureTest_3.java:23)
Caused by: java.lang.ArithmeticException: / by zero
at com.itzhimei.FutureTest_3.lambda$getPriceAsync$1(FutureTest_3.java:46)
at com.itzhimei.FutureTest_3$$Lambda$1/1323165413.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
*/
程序会先返回一个java.util.concurrent.ExecutionException异常,后面跟上代码实际的异常,这样,调用方也可以知道错误原因,并进行相应的异常处理。
3、使用whenComplete方法
CompletableFuture的whenComplete方法可以对结果再次进行处理,如果正常返回结果则返回,如果异常则可以定义异常处理逻辑进行处理。
CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)方法的参数是一个二元的消费函数,处理函数有两个参数void accept(T t, U u),第一个是业务逻辑处理结果,第二个是异常结果,二者其中一个总是为null。
例如:
//执行异常
String join2 = CompletableFuture.supplyAsync(() -> FutureTest_9.getTaskResultWithException(500, "Test Task 2"))
.whenComplete((x, e) -> {
System.out.println("计算结果:" + x);
System.out.println("异常信息:" + e);
}).join();
System.out.println(join2);
4、在whenComplete方法后面继续添加一个异常处理方法,这也是CompletableFuture的大多数方法都可行的异常处理方案。
//执行异常,捕获异常并处理
String join3 = CompletableFuture.supplyAsync(() -> FutureTest_10.getTaskResultWithException(500, "Test Task 2"))
.whenComplete((x, e) -> {
System.out.println("计算结果:" + x);
System.out.println("异常信息:" + e);
}).exceptionally(e-> "捕获异常,返回为空").join();
System.out.println(join3);