CompletableFuture 异步关于异常的坑

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

scss 复制代码
@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());
    }

}

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

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

java 复制代码
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()

csharp 复制代码
// 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()是阻塞的,在使用时要设置超时时间。

csharp 复制代码
// 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()

csharp 复制代码
// 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()

csharp 复制代码
// 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()

csharp 复制代码
// 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()

vbnet 复制代码
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实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

相关推荐
80530单词突击赢27 分钟前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法1 小时前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
WeiXiao_Hyy1 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇1 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
long3161 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
rannn_1112 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
短剑重铸之日2 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
Dragon Wu3 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
一个有梦有戏的人3 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
爬山算法4 小时前
Hibernate(88)如何在负载测试中使用Hibernate?
java·后端·hibernate