在项目中主要是用来抽取数据使用
遇到的问题
1.异步执行方法时,方法里面又异常信息,但没有异常日志输出
//配置线程池
@Configuration
public class MyConfiguration {
public static ThreadPoolExecutor getThreadPoolExecutor() {
int availableProcessors = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
availableProcessors,
availableProcessors,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(9999),
new ThreadFactoryBuilder().setNameFormat("custom-thread-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
//请求接口
@RestController
@RequestMapping("/dome")
public class DomeController {
private static Logger logger = LoggerFactory.getLogger(DomeController.class);
public static final ThreadPoolExecutor CUSTOM_THREAD_POOL = MyConfiguration.getThreadPoolExecutor();
@RequestMapping(value = "/testCompletableFuture",method = RequestMethod.GET)
public String testCompletableFuture(){
try {
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL);
} catch (Exception e) {
logger.error("异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
logger.info("========>testCompletableFuture finish");
return "SUCCESS";
}
}
上面这种写法执行请求之后并没有异常日志输出
解决办法
//将异步线程中的异常抛出到主线程中,这样就可以获取异常信息
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL).join();
//或者这样
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL).get();
//或者这样 设置超时时间
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL).get(2, TimeUnit.SECONDS);
注意使用exceptionally函数虽然能抛出异常,但并不是主线程抛出的,而是异步线程抛出的,主线程还是没有捕获异常,接口返回值还是成功的
try {
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL)
.exceptionally(e -> {
logger.error("异步运行异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
});
} catch (Exception e) {
logger.error("外层异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
调用异步函数whenComplete和handle里面抛出异常,效果同上
//whenComplete函数
try {
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL)
.whenComplete((r, e) -> {
if (e != null) {
logger.error("异步执行异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
});
} catch (Exception e) {
logger.error("外层异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
//handle函数
try {
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}, CUSTOM_THREAD_POOL)
.handle((r, e) -> {
if (e != null) {
logger.error("异步执行异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
return null;
});
} catch (Exception e) {
logger.error("外层异常信息: " + e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
如果说程序发生异常时我们不需要处理,则可以直接在异步方法后面直接调用get/join方法,将异常抛出到主线程中
总结
在使用异步CompletableFuture时,无论是否有返回值都要调用get()/join()方法,避免程序执行报错了,仍然返回成功。如果在程序报错时需要对上一个异步任务结果做其他操作,可以调用whenComplete()、handle()处理,如果只是对异常做处理,不涉及对上一个异步任务结果的情况,调用exceptionally()处理。