当前位置:   article > 正文

CompletableFuture 异步关于异常的坑!_completablefuture.runasync空指针

completablefuture.runasync空指针

自定义线程池

  1. @Configuration
  2. public class ThreadPoolConfig {
  3.     public static ThreadPoolExecutor getThreadPoolExecutor() {
  4.         int availableProcessors = Runtime.getRuntime().availableProcessors();
  5.         return new ThreadPoolExecutor(
  6.                 availableProcessors,
  7.                 availableProcessors,
  8.                 0L,
  9.                 TimeUnit.MILLISECONDS,
  10.                 new LinkedBlockingQueue<>(9999),
  11.                 new ThreadFactoryBuilder().setNameFormat("custom-thread-pool-%d").build(),
  12.                 new ThreadPoolExecutor.CallerRunsPolicy());
  13.     }
  14. }

程序存在异常,却返回成功

写一个存在异常的程序,让其异步执行

  1. public static final ThreadPoolExecutor CUSTOM_THREAD_POOL = ThreadPoolConfig.getThreadPoolExecutor();
  2. /**
  3.  * 异步执行异常测试
  4.  */
  5. @ApiOperation(value = "异步执行异常测试"code = 800)
  6. @GetMapping("/asyncException")
  7. public ResponseData<Object> asyncException() {
  8.     try {
  9.         try {
  10.             CompletableFuture.runAsync(() -> {
  11.                 int i = 1 / 0;
  12.             }, CUSTOM_THREAD_POOL);
  13.         } catch (Exception e) {
  14.             log.error("异常信息: " + e.getMessage(), e);
  15.             throw new BusinessException(e.getMessage());
  16.         }
  17.         return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
  18.     } catch (Exception e) {
  19.         return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
  20.     }
  21. }

结果:接口返回成功,控制台没有打印错误信息。

图片

异步调用join()

  1. // join方法获取异常信息: 将异步线程中发生的异常信息抛到主线程, 这样异常可被主线程捕获
  2. try {
  3.     CompletableFuture.runAsync(() -> {
  4.         int i = 1 / 0;
  5.     }, CUSTOM_THREAD_POOL).join();
  6. } catch (Exception e) {
  7.     log.error("外层异常信息: " + e.getMessage(), e);
  8.     throw new BusinessException(e.getMessage());
  9. }

结果:接口返回失败,控制台打印异常日志。

图片

异步调用get()

异步方法中get()是阻塞的,在使用时要设置超时时间。

  1. // get方法获取异常信息: 将异步线程中发生的异常信息抛到主线程, 这样异常可被主线程捕获
  2. try {
  3.     CompletableFuture.runAsync(() -> {
  4.         int i = 1 / 0;
  5.     }, CUSTOM_THREAD_POOL).get(2, TimeUnit.SECONDS);
  6. } catch (Exception e) {
  7.     log.error("外层异常信息: " + e.getMessage(), e);
  8.     throw new BusinessException(e.getMessage());
  9. }

结果:接口返回成功,控制台打印异常信息。

图片

异步调用exception()

  1. // exceptionally获取异常信息: 异常是存在于异步当中的, 不能被主线程捕获
  2. try {
  3.     CompletableFuture.runAsync(() -> {
  4.         int i = 1 / 0;
  5.     }, CUSTOM_THREAD_POOL)
  6.     .exceptionally(e -> {
  7.         log.error("异步运行异常信息: " + e.getMessage(), e);
  8.         throw new BusinessException(e.getMessage());
  9.     });
  10. } catch (Exception e) {
  11.     log.error("异常信息: " + e.getMessage(), e);
  12.     throw new BusinessException(e.getMessage());
  13. }

结果:接口返回成功,控制台打印异步线程异常日志,主线程没有打印异常日志

图片

异步调用whenComplete()

  1. // whenComplete获取异常信息: 异常是存在于异步当中的, 不能被主线程捕获
  2. try {
  3.     CompletableFuture.runAsync(() -> {
  4.         int i = 1 / 0;
  5.     }, CUSTOM_THREAD_POOL)
  6.     .whenComplete((r, e) -> {
  7.         if (e != null) {
  8.             log.error("异步执行异常信息: " + e.getMessage(), e);
  9.             throw new BusinessException(e.getMessage());
  10.         }
  11.     });
  12. } catch (Exception e) {
  13.     log.error("异常信息: " + e.getMessage(), e);
  14.     throw new BusinessException(e.getMessage());
  15. }

结果:结果返回成功,控制台打印异步线程异常信息,主线程没有打印异常信息。另外,搜索公众号Linux就该这样学后台回复“猴子”,获取一份惊喜礼包。

图片

异步调用handle()

  1. // handle获取异常信息: 异常是存在于异步当中的, 不能被主线程捕获
  2. try {
  3.     CompletableFuture.runAsync(() -> {
  4.         int i = 1 / 0;
  5.     }, CUSTOM_THREAD_POOL)
  6.     .handle((r, e) -> {
  7.         if (e != null) {
  8.             log.error("异步执行异常信息: " + e.getMessage(), e);
  9.             throw new BusinessException(e.getMessage());
  10.         }
  11.         return null;
  12.     });
  13. } catch (Exception e) {
  14.     log.error("异常信息: " + e.getMessage(), e);
  15.     throw new BusinessException(e.getMessage());
  16. }

结果:结果返回成功,控制台打印异步线程异常信息,主线程没有打印异常信息

图片

程序发生异常时需要做处理,可以调用get()/join()

  1. try {
  2.     CompletableFuture.runAsync(() -> {
  3.         int i = 1 / 0;
  4.     }, CUSTOM_THREAD_POOL)
  5.     .exceptionally(e -> {
  6.         log.error("异步执行异常信息: " + e.getMessage(), e);
  7.         throw new BusinessException(e.getMessage());
  8.     }).join();
  9. } catch (Exception e) {
  10.     log.error("异常信息: " + e.getMessage(), e);
  11.     throw new BusinessException(e.getMessage());
  12. }
  13. try {
  14.     CompletableFuture.runAsync(() -> {
  15.         int i = 1 / 0;
  16.     }, CUSTOM_THREAD_POOL)
  17.     .exceptionally(e -> {
  18.         log.error("异步执行异常信息: " + e.getMessage(), e);
  19.         throw new BusinessException(e.getMessage());
  20.     }).get(2, TimeUnit.SECONDS);
  21. } catch (Exception e) {
  22.     log.error("异常信息: " + e.getMessage(), e);
  23.     throw new BusinessException(e.getMessage());
  24. }

图片

程序发生异常时不做处理直接报错,直接调用get()/join()

直接的异步方法后调用get()/join()。

总结

在使用异步CompletableFuture时,无论是否有返回值都要调用get()/join()方法,避免程序执行报错了,仍然返回成功。如果在程序报错时需要对上一个异步任务结果做其他操作,可以调用whenComplete()handle()处理,如果只是对异常做处理,不涉及对上一个异步任务结果的情况,调用exceptionally()处理。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/824184
推荐阅读
相关标签
  

闽ICP备14008679号