CompletableFuture 异步关于异常的坑

发布时间:2024年01月18日

直接上代码!首先定义一个线程池的配置类!

@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("custom-thread-pool-%d").build(),
????????????????new?ThreadPoolExecutor.CallerRunsPolicy());
????}

}

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

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

public?static?final?ThreadPoolExecutor?CUSTOM_THREAD_POOL?=?ThreadPoolConfig.getThreadPoolExecutor();

/**
?*?异步执行异常测试
?*/
@ApiOperation(value?=?"异步执行异常测试",?code?=?800)
@GetMapping("/asyncException")
public?ResponseData<Object>?asyncException()?{
????try?{
????????try?{
????????????CompletableFuture.runAsync(()?->?{
????????????????int?i?=?1?/?0;
????????????},?CUSTOM_THREAD_POOL);
????????}?catch?(Exception?e)?{
????????????log.error("异常信息:?"?+?e.getMessage(),?e);
????????????throw?new?BusinessException(e.getMessage());
????????}
????????return?new?ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(),?"操作成功");
????}?catch?(Exception?e)?{
????????return?new?ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(),?"操作失败:"?+?e.getMessage());
????}
}

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

图片

异步调用join()

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

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

图片

异步调用get()

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

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

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

图片

图片

异步调用exception()

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

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

图片

图片

异步调用whenComplete()

//?whenComplete获取异常信息:?异常是存在于异步当中的,?不能被主线程捕获
try?{
????CompletableFuture.runAsync(()?->?{
????????int?i?=?1?/?0;
????},?CUSTOM_THREAD_POOL)
????.whenComplete((r,?e)?->?{
????????if?(e?!=?null)?{
????????????log.error("异步执行异常信息:?"?+?e.getMessage(),?e);
????????????throw?new?BusinessException(e.getMessage());
????????}
????});
}?catch?(Exception?e)?{
????log.error("异常信息:?"?+?e.getMessage(),?e);
????throw?new?BusinessException(e.getMessage());
}

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

图片

图片

异步调用handle()

//?handle获取异常信息:?异常是存在于异步当中的,?不能被主线程捕获
try?{
????CompletableFuture.runAsync(()?->?{
????????int?i?=?1?/?0;
????},?CUSTOM_THREAD_POOL)
????.handle((r,?e)?->?{
????????if?(e?!=?null)?{
????????????log.error("异步执行异常信息:?"?+?e.getMessage(),?e);
????????????throw?new?BusinessException(e.getMessage());
????????}
????????return?null;
????});
}?catch?(Exception?e)?{
????log.error("异常信息:?"?+?e.getMessage(),?e);
????throw?new?BusinessException(e.getMessage());
}

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

图片

图片

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

try?{
????CompletableFuture.runAsync(()?->?{
????????int?i?=?1?/?0;
????},?CUSTOM_THREAD_POOL)
????.exceptionally(e?->?{
????????log.error("异步执行异常信息:?"?+?e.getMessage(),?e);
????????throw?new?BusinessException(e.getMessage());
????}).join();
}?catch?(Exception?e)?{
????log.error("异常信息:?"?+?e.getMessage(),?e);
????throw?new?BusinessException(e.getMessage());
}

try?{
????CompletableFuture.runAsync(()?->?{
????????int?i?=?1?/?0;
????},?CUSTOM_THREAD_POOL)
????.exceptionally(e?->?{
????????log.error("异步执行异常信息:?"?+?e.getMessage(),?e);
????????throw?new?BusinessException(e.getMessage());
????}).get(2,?TimeUnit.SECONDS);
}?catch?(Exception?e)?{
????log.error("异常信息:?"?+?e.getMessage(),?e);
????throw?new?BusinessException(e.getMessage());
}

图片

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

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

总结

在使用异步CompletableFuture时,无论是否有返回值都要调用get()/join()方法,避免程序执行报错了,仍然返回成功。

如果在程序报错时需要对上一个异步任务结果做其他操作,可以调用whenComplete()handle()处理,如果只是对异常做处理,不涉及对上一个异步任务结果的情况,调用exceptionally()处理。

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记??就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

文章来源:https://blog.csdn.net/weixin_45334346/article/details/135661175
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。