CompletableFuture 异步关于异常的坑

f6d3a2e8276e34f9b9397f6df90602a3.jpeg来源:blog.csdn.net/weixin_43356538/article/details/129705507

👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 /  赠书福利

全栈前后端分离博客项目 1.0 版本完结啦,2.0 正在更新中..., 演示链接http://116.62.199.48/ ,全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了189小节,累计30w+字,讲解图:1308张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有800+小伙伴加入(早鸟价超低)

ad81a433aa6a59243b59d25127eb0542.gif

  • 自定义线程池

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

  • 异步调用join()

  • 异步调用get()

  • 异步调用exception()

  • 异步调用whenComplete()

  • 异步调用handle()

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

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

  • 总结


自定义线程池

@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());
    }
}

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

605de4d6d0ddee6195d4ce558ed682b5.png
图片

异步调用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());
}

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

693528716e90f6d6339085ea8570dd18.png
图片

异步调用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());
}

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

c4801d4bdf72a85efd06830d8e889408.png
图片

异步调用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());
}

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

21af5dad1cc495ac22186c928953f7fa.png
图片

异步调用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());
}

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

5db3d661db6142425d0941172fdf2d38.png
图片

异步调用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());
}

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

e66bf229f47a3b8206a6d392517b98e2.png
图片

程序发生异常时需要做处理,可以调用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());
}
433220e19232658cb235c6d52269b96d.png
图片

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

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

总结

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

👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 /  赠书福利

全栈前后端分离博客项目 1.0 版本完结啦,2.0 正在更新中..., 演示链接http://116.62.199.48/ ,全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了189小节,累计30w+字,讲解图:1308张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有800+小伙伴加入(早鸟价超低)

bc841fccef5998e054b875a159b68506.gif

f20a7dd123437db2c76f59ac98331548.jpeg

 
 

d978fe61559c6bb862edad0de5668a5f.gif

 
 
 
 
1. 我的私密学习小圈子~
2. 一碰就头疼的 Kafka 消息重复问题,立马解决!
3. Java 微服务随机掉线排查过程
4. 优雅的实现接口防刷,最强方案来了~!
 
 
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值