玩转CompletableFuture异步编程

  • CompletableFuture.supplyAsync 表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable task) 方法
  • CompletableFuture.runAsync 表示创建无返回值的异步任务
  • CompletableFuture.thenApplyCompletableFuture.thenApplyAsync表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中
    (注:job1执行结束后,将job1的方法返回值作为入参传递到job2中并立即执行job2。thenApplyAsync与thenApply的区别在于,前者是将job2提交到线程池中异步执行,实际执行job2的线程可能是另外一个线程,后者是由执行job1的线程立即执行job2,即两个job都是同一个线程执行的)
  • CompletableFuture.thenAccept thenAccept 同 thenApply 接收上一个任务的返回值作为参数,但是无返回值
  • CompletableFuture.thenRun thenRun 的方法没有入参,也没有返回值
  • CompletableFuture.exceptionallyexceptionally方法指定某个任务执行异常时执行的回调方法,会将抛出异常作为参数传递到回调方法中。
  • CompletableFuture.whenComplete是当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常

测试案例

  @Test
    public void test10() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
          if(true){
                throw new RuntimeException("test");
            }else{
                return 1.2;
            }
        });
        //cf执行完成后会将执行结果和执行过程中抛出的异常传入回调方法,如果是正常执行的则传入的异常为null
        //所以下面两个参数a,b 分别对应cf的返回值,cf抛出的异常。一般而言,如果cf运行正常没有出异常的情况下,a就有值,而b就是null。而如果cf里面抛异常了,那a就是null,b就是异常参数传进来
        CompletableFuture<Double> cf2=cf.whenComplete((a,b)->{
            if(b!=null){
                log.error("cf执行异常,异常信息={}",b.getMessage());
            }else{
                log.info("cf执行正常,没有抛任何异常。且cf执行的结果={}",a);
            }
        });
        //如果cf是正常执行的,cf2.get的结果就是cf执行的结果
        //如果cf是执行异常,则cf2.get会抛出异常
        System.out.println("cf2的结果="+cf2.get());
    }
  • CompletableFuture.handle 跟whenComplete函数基本一致,区别在于handle的回调方法有返回值,必须要return一个值,且handle方法得到的CompletableFuture的执行结果与前一个CompletableFuture的执行结果无关
    测试案例
 @Test
    public void test10() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            if(true){
                throw new RuntimeException("test");
            }else{
                return 1.2;
            }
        });
        //cf执行完成后会将执行结果和执行过程中抛出的异常传入回调方法,如果是正常执行的则传入的异常为null
        CompletableFuture<String> cf2=cf.handle((a,b)->{
            if(b!=null){
                log.error("cf执行异常,异常信息={}",b.getMessage());
            }else{
                log.info("cf执行正常,没有抛任何异常。且cf执行的结果={}",a);
            }
            //之所以加下面的代码,是因为handle函数里面需要有return返回值才行
            if(b!=null){
                return "run error";
            }else{
                return "run succ";
            }

        });
        //get的结果是cf2的返回值,跟cf没关系了
        System.out.println("cf2的返回值="+cf2.get()); 
        //打印结果:  cf2的返回值=run error
    }
  • CompletableFuture.thenCombineCompletableFuture.thenAcceptBothCompletableFuture.runAfterBoth
    这三个方法都是将两个CompletableFuture组合起来,只有当那两个CompletableFuture都正常执行完了才会执行某个任务,区别在于,thenCombine会将两个任务的执行结果作为方法入参传递到指定方法中,且该方法有返回值;thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为入参传递到这3个方法中。
  @Test
    public void test7() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            return 1.2;
        });
        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            return 3.2;
        });
        //cf和cf2的异步任务都执行完成后,会将其执行结果作为方法入参传递给cf3,且有返回值
        CompletableFuture<Double> cf3=cf.thenCombine(cf2,(a,b)->{
            log.info("thenCombine,a={},b={}",a,b);
            //打印结果a=1.2,b=3.2
            return a+b;
        });

        //cf和cf2的异步任务都执行完成后,会将其执行结果作为方法入参传递给cf3,无返回值
        CompletableFuture cf4=cf.thenAcceptBoth(cf2,(a,b)->{
            log.info("thenAcceptBoth,a={},b={}",a,b);
            //打印结果a=1.2,b=3.2
        });

        //cf4和cf3都执行完成后,执行cf5,无入参,无返回值
        CompletableFuture cf5=cf4.runAfterBoth(cf3,()->{
          log.info("runAfterBoth,无入参,无返回值");
        });
        //等待子任务执行完成
        System.out.println("cf的返回结果="+cf.get());
        System.out.println("cf2的返回结果="+cf2.get());
        System.out.println("cf3的返回结果="+cf3.get());
        System.out.println("cf4的返回结果="+cf4.get());
        System.out.println("cf5的返回结果="+cf5.get());
        //打印结果:
      //cf的返回结果=1.2
      //cf2的返回结果=3.2
      //cf3的返回结果=4.4
      //cf4的返回结果=null
      //cf5的返回结果=null
    }
  • CompletableFuture.applyToEitherCompletableFuture. acceptEither 还有CompletableFuture. runAfterEither。 这三个方法都是将两个CompletableFuture组合起来,只要其中一个CompletableFuture执行完了就会执行某个任务,其区别在于applyToEither会将已经执行完成的任务的执行结果作为方法入参,并有返回值;acceptEither同样将已经执行完成的任务的执行结果作为方法入参,但是没有返回值;runAfterEither没有方法入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。
   @Test
    public void test8() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            int i = random.nextInt()%10;
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("cf执行完毕");
            return 1.2;
        });
        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
            int i = random.nextInt()%10;
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("cf2执行完毕");
            return 3.2;
        });
        //cf和cf2中任何一个先执行完,会将其执行结果作为方法入参传递给cf3,且有返回值
        CompletableFuture<Double> cf3=cf.applyToEither(cf2,(result)->{
            log.info("cf3开始执行了,接收到的result={},当前时间={}",result,LocalDateTime.now());
            int i = random.nextInt()%10;
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("cf3开始执行完毕");
            return result;
        });

        //cf和cf2的任何一个先执行完会将其执行结果作为方法入参传递给cf4,无返回值
        CompletableFuture cf4=cf.acceptEither(cf2,(result)->{
            log.info("cf4开始执行了,接收到的result={},当前时间={}",result,LocalDateTime.now());
            int i = random.nextInt()%10;
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("cf4开始执行完毕");
        });

        //cf4和cf3中任何一个执行完就立刻执行cf5,无入参,无返回值
        CompletableFuture cf5=cf4.runAfterEither(cf3,()->{
            log.info("cf5开始执行了");
        });

        try {
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

测试结果:
在这里插入图片描述

  • CompletableFuture.thenCompose 这个函数跟CompletableFuture.thenApply 很相似。但是有点区别,thenApply()转换的是泛型中的类型,相当于将CompletableFuture<T> 转换生成新的CompletableFuture<U>
    而thenCompose()用来连接两个CompletableFuture,是生成一个新的CompletableFuture。
  @Test
    public void test9() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            return 1.2;
        });
        CompletableFuture<String> cf2= cf.thenCompose((param)->{
            log.info("param={}",param);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            return CompletableFuture.supplyAsync(()->{
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                return "job3 test";
            });
        });
      log.info("cf2的结果={}",cf2.get());
    }

运行结果
在这里插入图片描述

  • CompletableFuture.allOf 返回的CompletableFuture是多个任务都执行完成后才会执行,只要有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。
 // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            throw new RuntimeException("test");
            //return 1.2;
        });
        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            return 2.2;
        });
        CompletableFuture<Double> cf3 = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(1300);
            } catch (InterruptedException e) {
            }
            return 3.2;
        });
        //allof等待所有任务执行完成才执行cf4,如果有一个任务异常终止,则cf4.get时会抛出异常,都是正常执行,cf4.get返回null
        
        //如果cf,cf2,cf3中至少有一个任务出现了异常,那么下面的a就是null,b就是异常信息,如果所有任务都正常,那a也是null,b是null
        CompletableFuture cf4=CompletableFuture.allOf(cf,cf2,cf3).whenComplete((a,b)->{
            if(b!=null){
             log.error("cf,cf2,cf3中至少有一个任务出现了异常,异常信息={}",b.getMessage());
            }else{
                log.error("cf,cf2,cf3任务全部执行成功,a={}",a);
            }
        });
        log.info("cf4执行完毕,结果={}",cf4.get());
    }

运行结果
在这里插入图片描述

  • CompletableFuture.anyOf anyOf 的含义是只要有任意一个 CompletableFuture 结束,就可以做 接下来的事情,而无须像 AllOf 那样,等待所有的 CompletableFuture 结束。但由于每个 CompletableFuture 的返回值类型都可能不同,任意一个, 意味着无法判断是什么类型,所以 anyOf 的返回值是 CompletableFuture<Object>类型
 @Test
    public void test11() throws Exception {
        // 创建异步执行任务:
        CompletableFuture<String> cf = CompletableFuture.supplyAsync(()->{
            log.info("cf执行开始了");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            log.info("cf执行开始结束");
           return  "xiaoming";
        });
        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
            log.info("cf2执行开始了");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            log.info("cf2执行开始结束");
            return 2.2;
        });
        CompletableFuture<Double> cf3 = CompletableFuture.supplyAsync(()->{
            log.info("cf3执行开始了");
            try {
                Thread.sleep(1300);
            } catch (InterruptedException e) {
            }
            log.info("cf3执行开始结束");
            return 3.2;
        });
        //anyOf是只有一个任务执行完成,无论是正常执行或者执行异常,都会执行cf4,cf4.get的结果就是已执行完成的任务的执行结果
        CompletableFuture<Object> cf4=CompletableFuture.anyOf(cf,cf2,cf3).whenComplete((a,b)->{
            if(b!=null){
                log.info("cf4执行开始,a={},b={}",a,b.getMessage());
            }else{
                log.info("cf4执行开始,a={}",a);
            }

        });
        log.info("cf4执行结果={}",cf4.get());
        TimeUnit.SECONDS.sleep(10);
    }

运行结果
在这里插入图片描述

CompletableFuture默认使用ForkJoinPool.commonPool()线程池,如果机器是单核的,则默认使用ThreadPerTaskExecutor,该类是一个内部类,每次执行execute都会创建一个新线程

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CompletableFuture是Java 8引入的一种异步编程工具,它可以帮助我们更方便地处理异步任务和并发操作。下面是一些玩转CompletableFuture的方法和技巧: 1. 创建CompletableFuture对象:可以使用CompletableFuture的静态工厂方法,比如CompletableFuture.supplyAsync()创建一个带有返回值的CompletableFuture对象,CompletableFuture.runAsync()创建一个不带返回值的CompletableFuture对象。 2. 组合多个CompletableFuture:可以使用thenCompose()方法将多个CompletableFuture进行串行组合,也可以使用thenCombine()方法将多个CompletableFuture进行并行组合。 3. 异常处理:使用exceptionally()方法可以在CompletableFuture发生异常时进行处理,使用handle()方法可以对异常进行处理并返回一个默认值。 4. 超时处理:使用orTimeout()方法可以设置CompletableFuture的超时时间,如果在指定时间内任务没有完成,则会抛出TimeoutException。 5. 并行处理:使用allOf()方法可以并行执行多个CompletableFuture,并等待它们全部完成。 6. 异步回调:可以使用whenComplete()、handle()、exceptionally()等方法来注册异步回调函数,在任务完成时执行相应的操作。 7. 取消任务:使用completeExceptionally()方法可以取消CompletableFuture任务的执行。 这些只是CompletableFuture的一些常见用法和技巧,你可以根据具体的需求进一步探索和玩转CompletableFuture。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java全栈研发大联盟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值