CompletableFuture使用详解

CompletableFuture引入
CompletableFuture是java8引入的一个用于异步编程的工具类。
我们根据CompletableFuture的简单示例来逐步介绍它的强大功能。

        final CompletableFuture<String> cf = new CompletableFuture<String>();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println(cf.get());
                    System.out.println("thread exit ...");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(cf.join()); // 抛出未检查异常unchecked exception,即RuntimeException,其它与get差不多,阻塞获取值
            }
        });
        t.setDaemon(true);
        t.start();
        cf.complete("finish ... ");

上述例子,我们可以发现CompletableFuture它也是一个future,可以通过future.get()来获取到结果。
CompletableFuture还提供了join()方法,它的功能和get()方法是一样的,都是阻塞获取值,它们的区别在于 join() 抛出的是 unchecked Exception。
t.setDaemon和睡眠时间1秒是为了保证主线程在子线程执行完前结束,后台线程(子线程)自动结束,不打印结果。

下面介绍几个static静态方法,它们使用任务来实例化一个CompletableFuture实例

CompletableFuture.runAsync(Runnable runnable);
CompletableFuture.runAsync(Runnable runnable, Executor executor);

CompletableFuture.supplyAsync(Supplier<U> supplier);
CompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor);


CompletableFuture.runAsync(new Runnable(){
      @Override
      public void run() {
          System.out.println(Thread.currentThread().isDaemon());
          System.out.println("runAsync task ...");
      }
});

CompletableFuture future = CompletableFuture.supplyAsync(() -> "resultA");
System.out.println(future.get());

​

runAsync接收Runnable实例,并没有返回值。
supplyAsync接收Supplier,它是有返回值的。
两方法都可以自定义线程池,表示让任务在自定义线程池中执行,不指定的话,在ForkJoinPool.commonPool()中执行。
注意:线程池中线程默认都是Deamon线程,上面runAsync在main主函数执行时不一定能打印出来。

任务顺序执行
我们可以考虑下执行两个任务的情况,首先执行任务A,然后将任务A的结果传递给任务B。
任务之间的执行存在很多种情况:

  • 任务A是否有返回值
  • 任务B是否需要接收任务A的返回值
  • 任务B是否有返回值

不过可以明确的是,肯定是任务A执行完再执行任务B

CompletableFuture.runAsync(() -> {}).thenRun(() -> {}); 
CompletableFuture.runAsync(() -> {}).thenAccept(resultA -> {}); 
CompletableFuture.runAsync(() -> {}).thenApply(resultA -> "resultB");

CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {});
CompletableFuture.supplyAsync(() -> "resultA").thenAccept(resultA -> {});
CompletableFuture.supplyAsync(() -> "resultA").thenApply(resultA -> resultA + " resultB");

前3行代码演示的是,任务A无返回值,所以对应的第2行和第3行代码中,resultA其实是 null
第4行用的是 thenRun(Runnable runnable),任务A执行完执行B,并且B不需要 A 的结果。
第5行用的是 thenAccept(Consumer action),任务A执行完执行B,B需要A的结果,但是任务B不返回值。
第6行用的是 thenApply(Function fn),任务A执行完执行B,B需要A的结果,同时任务B有返回值。
注意:如果任务B后面还有任务C,往下继续调用 .thenXxx() 即可。

异常处理
CompletableFuture的异常处理主要介绍如下两个方法:

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "resultA")
                .thenApply(resultA -> resultA + " resultB")
                .thenApply(resultB -> resultB + " resultC")
                .thenApply(resultC -> resultC + " resultD");
        System.out.println(future.join());
        // 打印结果:resultA resultB resultC resultD


        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            throw new RuntimeException();
        })
                .exceptionally(ex -> "errorResultA")
                .thenApply(resultA -> resultA + " resultB")
                .thenApply(resultB -> resultB + " resultC")
                .thenApply(resultC -> resultC + " resultD");
        System.out.println(future.join());
        // 打印结果:errorResultA resultB resultC resultD


        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "resultA")
                .thenApply(resultA -> resultA + " resultB")
                .thenApply(resultB -> {
                    // 此处任务C抛出异常
                    throw new RuntimeException();
                })
                // 处理任务C的返回值或者异常
                .handle(new BiFunction<Object, Throwable, Object>() {
                    @Override
                    public Object apply(Object re, Throwable throwable) {
                        if (throwable != null) {
                            return "errorResultC";
                        }
                        return re;
                    }
                })
                .thenApply(resultC -> resultC + " resultD");
        System.out.println(future.join());
  • 方式1返回结果是正常的。
  • 方式2通过exceptionally解决异常问题,并返回新的结果,这个新的结果将传递给任务B。
  • 方式3通过handle解决异常问题,handle处理任务C抛出的异常, re和throwable必然有一个是null,分别代表正常的执行结果和异常的情况。注意(re和throwable也有可能都为空,如果任务C没有返回值情况下)。
     

合并两个任务结果
前面我们讲述了任务的顺序执行,后一个任务执行依赖于前一个任务返回值。
这里讲述任务之间的并行处理,没有先后顺序区分,合并各个任务处理后的结果,以便进行后续操作。

        CompletableFuture<String> cfA = CompletableFuture.supplyAsync(() -> "resultA");
        CompletableFuture<String> cfB = CompletableFuture.supplyAsync(() -> "resultB");

        cfA.thenAcceptBoth(cfB, (resultA, resultB) -> {
            System.out.println(resultA + " " + resultB);
        });

        CompletableFuture<String> cf = cfA.thenCombine(cfB, (resultA, resultB) -> resultA + " " + resultB);
        System.out.println(cf.get());

        cfA.runAfterBoth(cfB, () -> {
            System.out.println("runAfterBoth ... ");
        });
  • thenAcceptBoth表示后续的处理不会返回值
  • thenCombine表示需要返回值
  • runAfterBoth表示不需要任务A,B的返回值


取多个任务结果
这里介绍两个非常简单的静态方法:allOf()和anyOf()方法

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs){...}
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {...}
        CompletableFuture cfA = CompletableFuture.supplyAsync(() -> "resultA");
        CompletableFuture cfB = CompletableFuture.supplyAsync(() -> 123);
        CompletableFuture cfC = CompletableFuture.supplyAsync(() -> "resultC");

        CompletableFuture<Void> future1 = CompletableFuture.allOf(cfA, cfB, cfC);
        future1.join();

        CompletableFuture<Object> future2 = CompletableFuture.anyOf(cfA, cfB, cfC);
        Object result = future2.join();
        System.out.println(result);
  • allOf、anyOf区别
  • allOf聚合了多个CompletableFuture实例,它没有返回值。future1.join()的调用将阻塞,直到所有任务执行结束。
  • anyOf其中任意一个任务完成,future2.join就会返回值,它总是返回最先完成的任务。

either
相比于anyOf, 如果只需要处理两个CompletableFuture实例,可以使用xxxEither方法。

        CompletableFuture cfA = CompletableFuture.supplyAsync(() -> "resultA");
        CompletableFuture cfB = CompletableFuture.supplyAsync(() -> 123);

        // 没有返回值,result随机值(resultA或者123)
        cfA.acceptEither(cfB, result -> {
            System.out.println(result);
        });
        cfA.acceptEitherAsync(cfB, result -> {});
        cfA.acceptEitherAsync(cfB, result -> {}, executorService);

        // 有返回值,返回result随机值(resultA或者123)
        CompletableFuture cf = cfA.applyToEither(cfB, result -> {return result;});
        System.out.println(cf.get());
        cfA.applyToEitherAsync(cfB, result -> {return result;});
        cfA.applyToEitherAsync(cfB, result -> {return result;}, executorService);

         // 不需要任务A、B的执行结果,没有返回值
        cfA.runAfterEither(cfB, () -> {
            System.out.println("runAfterEither ... ");
        });
        cfA.runAfterEitherAsync(cfB, () -> {});
        cfA.runAfterEitherAsync(cfB, () -> {}, executorService);

带either的方法,指的是两个任务中的其中一个执行完成,就执行指定的操作。上面几组的区别,在上述代码中已有注释。
几个注意点如下:
cfA.acceptEither(cfB, result -> {}); 和cfB.acceptEither(cfA, result -> {}); 意思一致,只是写法上的区别。

同组各个方法的区别

  •  方法一:它由任务A或任务B所在的执行线程来执行,取决于哪个任务先结束,并非同步
  •  方法二:带Async后缀方法,代表将需要执行的任务放到 ForkJoinPool.commonPool()中执行
  •  方法三:将任务放到指定线程池中执行

compose
前面介绍了thenAcceptBoththenCombine用于聚合两个任务,其实compose也是一样的功能,它们本质上都是为了让多个 CompletableFuture实例形成一个链。

        CompletableFuture<String> cfA = CompletableFuture.supplyAsync(() -> {
            System.out.println("processing a...");
            return "hello";
        });

        CompletableFuture<String> cfB = CompletableFuture.supplyAsync(() -> {
            System.out.println("processing b...");
            return " world";
        });

        CompletableFuture<String> cfC = CompletableFuture.supplyAsync(() -> {
            System.out.println("processing c...");
            return ", I'm robot!";
        });

        CompletableFuture cf = cfA.thenCombine(cfB, (resultA, resultB) -> {
            return resultA + resultB;
        }).thenCombine(cfC, (resultAB, resultC) -> {
            return resultAB + resultC;
        });
        System.out.println(cf.get()); // hello world, I'm robot



        CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
            // 第一个实例的结果
            return "hello";
        }).thenCompose(resultA -> CompletableFuture.supplyAsync(() -> {
            // 把上一个实例的结果传递到这里
            return resultA + " world";
        })).thenCompose(resultAB -> CompletableFuture.supplyAsync(() -> {
            return resultAB + ", I'm robot";
        }));
        System.out.println(result.join()); // hello world, I'm robot

thenCombine和thenCompose区别
thenCombine的cfA、cfB、cfC之间完全不存在数据依赖关系,执行顺序随机,只不过是按照指定结果聚合在了一起。
thenCompose后一个实例能获取到前一个实例执行的结果,执行有序。

thenApply和thenCompose的区别

        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> "hello")
                .thenApply(cfA -> cfA + " world");
        System.out.println(future1.get());

        CompletableFuture<String> future2 = CompletableFuture
                .supplyAsync(() -> "hello")
                .thenCompose(cfA -> CompletableFuture.supplyAsync(() -> cfA + " world"));
        System.out.println(future2.get());

两者都接收一个Function
thenApply中返回一个具体值,thenCompose返回一个cf实例
thenApply同步,thenCompose异步

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值