CompletableFuture学习使用

在项目中主要是用来抽取数据使用

遇到的问题

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()处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值