CompleteFuture异步执行失败却返回成功
1、自定义线程池
@Configuration
public class ThreadPoolConfig {
public static ThreadPoolExecutor getThreadPoolExecutor() {
int availableProcessors = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
availableProcessors,
availableProcessors,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(9999),
new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
编写三个简单的异步任务,在第三个任务执行时制造异常
public static final ThreadPoolExecutor threadPoolExecutor = ThreadPoolConfig.getThreadPoolExecutor();
/**
* 异步任务编排
*/
@GetMapping("/asyncTaskArrange")
public ResponseData<Object> asyncTaskArrange() {
try {
CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
.thenRunAsync(() -> {
log.info("任务3");
int i = 1 / 0;
}, threadPoolExecutor)
.exceptionally(e -> {
log.error("异常信息: " + e.getMessage(), e);
throw new BusinessException(e.getMessage());
});
return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
} catch (Exception e) {
log.error("程序异常信息: " + e.getMessage(), e);
return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
}
}
程序执行结果
控制台却打印了异常信息,异步任务自己捕获并抛出了异常信息,但是最外成决定程序执行成功失败的try-catch没有捕获到异常信息,所以返回成功。
解决办法就是让异步任务生成CompleteFuture,并调用get()方法或者join(),注意:异步任务中get()是阻塞的,使用时需要添加超时时间。
/**
* 异步任务编排
*/
@GetMapping("/asyncTaskArrange")
public ResponseData<Object> asyncTaskArrange() {
try {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
.thenRunAsync(() -> {
log.info("任务3");
int i = 1 / 0;
}, threadPoolExecutor)
.exceptionally(e -> {
log.error("异常信息: " + e.getMessage(), e);
throw new BusinessException(e.getMessage());
});
// future.get(10, TimeUnit.SECONDS);
future.join();
return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
} catch (Exception e) {
log.error("程序异常信息: " + e.getMessage(), e);
return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
}
}
再次执行程序,就能返回接口正确的执行结果
异步任务异常信息处理方法
handle
handle需要在可能发生异常的异步方法后调用,e表示上一个异步任务的异常信息,如果为null,表示执行成功,否则发现异常,需要抛出异常信息。
/**
* handle处理异步异常
*/
@GetMapping("/asyncHandle")
public ResponseData<Object> asyncHandle() {
try {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
.thenRunAsync(() -> {
log.info("任务3");
int i = 1 / 0;
}, threadPoolExecutor)
.handle((result, e) -> {
if (e != null) {
log.error("异常信息: " + e.getMessage(), e);
throw new BusinessException(e.getMessage());
} else {
return null;
}
});
// future.get(10, TimeUnit.SECONDS);
future.join();
return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
} catch (Exception e) {
log.error("程序异常信息: " + e.getMessage(), e);
return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
}
}
现在改变handle的位置,放在不会方式异常的异步任务之后,再次进行测试,依然能捕获到异常,所以handle是会捕获一整个编排的异步任务链。
/**
* handle处理异步异常
*/
@GetMapping("/asyncHandle")
public ResponseData<Object> asyncHandle() {
try {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
.handle((result, e) -> {
if (e != null) {
log.error("异常信息: " + e.getMessage(), e);
throw new BusinessException(e.getMessage());
} else {
return null;
}
})
.thenRunAsync(() -> {
log.info("任务3");
int i = 1 / 0;
}, threadPoolExecutor);
// future.get(10, TimeUnit.SECONDS);
future.join();
return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
} catch (Exception e) {
log.error("程序异常信息: " + e.getMessage(), e);
return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
}
}
whenComplete
whenComplete任务执行完毕之后的回调方法,也是通过入参中的e来判断上一个异步任务是否存在异常。
/**
* handle处理异步异常
*/
@GetMapping("/asyncWhenComplete")
public ResponseData<Object> asyncWhenComplete() {
try {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
.thenRunAsync(() -> {
log.info("任务3");
int i = 1 / 0;
}, threadPoolExecutor)
.whenComplete((result , e) -> {
if (e != null) {
log.error("异常信息: " + e.getMessage(), e);
throw new BusinessException(e.getMessage());
}
});
// future.get(10, TimeUnit.SECONDS);
future.join();
return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
} catch (Exception e) {
log.error("程序异常信息: " + e.getMessage(), e);
return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
}
}
exceptionally
如果编排的异步任务执行异常会执行exceptionally,如果执行正常,会返回执行正常的结果。