CompleteFuture异步执行失败却返回成功

CompleteFuture异步执行失败却返回成功

1、自定义线程池

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

}

编写三个简单的异步任务,在第三个任务执行时制造异常

public static final ThreadPoolExecutor threadPoolExecutor = ThreadPoolConfig.getThreadPoolExecutor();

/**
 * 异步任务编排
 */
@GetMapping("/asyncTaskArrange")
public ResponseData<Object> asyncTaskArrange() {
    try {
        CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
                .thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
                .thenRunAsync(() -> {
                    log.info("任务3");
                    int i = 1 / 0;
                }, threadPoolExecutor)
                .exceptionally(e -> {
                    log.error("异常信息: " + e.getMessage(), e);
                    throw new BusinessException(e.getMessage());
                });

        return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
    } catch (Exception e) {
        log.error("程序异常信息: " + e.getMessage(), e);
        return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
    }
}

程序执行结果
在这里插入图片描述
控制台却打印了异常信息,异步任务自己捕获并抛出了异常信息,但是最外成决定程序执行成功失败的try-catch没有捕获到异常信息,所以返回成功。
在这里插入图片描述
解决办法就是让异步任务生成CompleteFuture,并调用get()方法或者join(),注意:异步任务中get()是阻塞的,使用时需要添加超时时间。

/**
 * 异步任务编排
 */
@GetMapping("/asyncTaskArrange")
public ResponseData<Object> asyncTaskArrange() {
    try {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
                .thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
                .thenRunAsync(() -> {
                    log.info("任务3");
                    int i = 1 / 0;
                }, threadPoolExecutor)
                .exceptionally(e -> {
                    log.error("异常信息: " + e.getMessage(), e);
                    throw new BusinessException(e.getMessage());
                });
        // future.get(10, TimeUnit.SECONDS);
        future.join();

        return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
    } catch (Exception e) {
        log.error("程序异常信息: " + e.getMessage(), e);
        return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
    }
}

再次执行程序,就能返回接口正确的执行结果
在这里插入图片描述

在这里插入图片描述

异步任务异常信息处理方法

handle

handle需要在可能发生异常的异步方法后调用,e表示上一个异步任务的异常信息,如果为null,表示执行成功,否则发现异常,需要抛出异常信息。

/**
	 * handle处理异步异常
	 */
	@GetMapping("/asyncHandle")
	public ResponseData<Object> asyncHandle() {
		try {
			CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
					.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
					.thenRunAsync(() -> {
						log.info("任务3");
						int i = 1 / 0;
					}, threadPoolExecutor)
					.handle((result, e) -> {
						if (e != null) {
							log.error("异常信息: " + e.getMessage(), e);
							throw new BusinessException(e.getMessage());
						} else {
							return null;
						}
					});
			// future.get(10, TimeUnit.SECONDS);
			future.join();

			return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
		} catch (Exception e) {
			log.error("程序异常信息: " + e.getMessage(), e);
			return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
		}
	}

在这里插入图片描述
现在改变handle的位置,放在不会方式异常的异步任务之后,再次进行测试,依然能捕获到异常,所以handle是会捕获一整个编排的异步任务链。

/**
	 * handle处理异步异常
	 */
	@GetMapping("/asyncHandle")
	public ResponseData<Object> asyncHandle() {
		try {
			CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
					.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
					.handle((result, e) -> {
						if (e != null) {
							log.error("异常信息: " + e.getMessage(), e);
							throw new BusinessException(e.getMessage());
						} else {
							return null;
						}
					})
					.thenRunAsync(() -> {
						log.info("任务3");
						int i = 1 / 0;
					}, threadPoolExecutor);
			// future.get(10, TimeUnit.SECONDS);
			future.join();

			return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
		} catch (Exception e) {
			log.error("程序异常信息: " + e.getMessage(), e);
			return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
		}
	}

在这里插入图片描述

whenComplete

whenComplete任务执行完毕之后的回调方法,也是通过入参中的e来判断上一个异步任务是否存在异常。

/**
	 * handle处理异步异常
	 */
	@GetMapping("/asyncWhenComplete")
	public ResponseData<Object> asyncWhenComplete() {
		try {
			CompletableFuture<Void> future = CompletableFuture.runAsync(() -> log.info("任务1"), threadPoolExecutor)
					.thenRunAsync(() -> log.info("任务2"), threadPoolExecutor)
					.thenRunAsync(() -> {
						log.info("任务3");
						int i = 1 / 0;
					}, threadPoolExecutor)
					.whenComplete((result , e) -> {
						if (e != null) {
							log.error("异常信息: " + e.getMessage(), e);
							throw new BusinessException(e.getMessage());
						}
					});
			// future.get(10, TimeUnit.SECONDS);
			future.join();

			return new ResponseData<>(StatusCodeEnum.SUCCESS_CODE.getStatusCode(), "操作成功");
		} catch (Exception e) {
			log.error("程序异常信息: " + e.getMessage(), e);
			return new ResponseData<>(StatusCodeEnum.ERROR_CODE.getStatusCode(), "操作失败:" + e.getMessage());
		}
	}

exceptionally

如果编排的异步任务执行异常会执行exceptionally,如果执行正常,会返回执行正常的结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值