【无标题】

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("执行过程=="+Thread.currentThread().getName());
try {
    Thread.sleep(3000);
                return "123";
            } catch (Exception e) {
                e.printStackTrace();
                return "456";
            }
        }).thenApply(
                result ->{
                    System.out.println("thenApply"+Thread.currentThread().getName());
                    System.out.println(result);
                    return "";
                }).exceptionally(err ->{
            System.out.println("发生异常");
            return "";
        });
        System.out.println("获取结果=="+Thread.currentThread().getName());

    }
}

如上代码,

        模拟先异步调用一个接口,然后后边的调用步骤依赖前面一个接口的返回值,如果不加sleep,那后边thenapply里的调用是main主线程去调用的,会立马返回 日志也会打印,因为主线程的执行过程完整记录下来了,但是如果加了sleep,也就是第一个接口调用的耗时比较长,下一个接口等待就会阻塞,completeableFuture会启用异步线程(如果加了线程池会使用自定义的线程池,没添加的话使用forkjoin线程池),需要注意的是

控制台的日志不会打印第二个接口调用打印的"thenApply",因为控制台打印的是主线程的日志,主线程因为调用完第一个接口立马结束不等待返回值,所以生命周期较短,而异步回调线程在等待3秒后回来打印的时候,主线程已经结束,控制台看不到异步线程的日志了。

结论:

线程执行问题

CompletableFuture实现了CompletionStage接口,通过丰富的回调方法,支持各种组合操作,每种组合场景都有同步和异步两种方法。

同步方法(即不带Async后缀的方法)有两种情况。

如果注册时被依赖的操作已经执行完成,则直接由当前线程执行

如果注册时被依赖的操作还未执行完,则由回调线程执行

异步方法(即带Async后缀的方法):可以选择是否传递线程池参数Executor运行在指定线程池中;当不传递Executor时,会使用ForkJoinPool中的共用线程池CommonPool(CommonPool的大小是CPU核数-1,如果是IO密集的应用,线程数可能成为瓶颈)。

建议强制传线程池,且根据实际情况做线程池隔离。

如果注册时被依赖的操作已经执行完成,则直接由当前线程执行

如果注册时被依赖的操作还未执行完,则由回调线程执行

以上两点可以保证在调用多个步骤的方法时能够快速结束调用,如果第一步结束了那就由主线程调用,如果没结束会等到异步线程回调来了之后去调用,所以不会阻塞主线程的继续执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值