Java基础 : CompletableFuture① 基础使用

一、前言

本文内容参考:CompletableFuture基本使用,建议阅读原文。

本文在个人理解基础上有部分删改,仅用于个人学习记录,限于个人能力本文可能出现错漏,因此再次建议阅读原文 CompletableFuture基本使用


CompletableFuture 是在阅读 Dubbo 异步调用时接触到的内容,当时一直对 CompletableFuture 进行了简单的学习,碍于时间原因一直没有记录,趁现在有时间对 CompletableFuture 进行更深入的学习并记录。

二、介绍

CompletableFuture基本使用 文章中对 CompletableFuture 的介绍 :

CompletableFuture是什么

  • CompletableFuture是Java8中提供的Future的扩展功能,简化异步编程的复杂性;
  • 引入函数式编程,通过回调的方式处理计算结果,也提供了转换和组合的方法;
  • 它实现了Future和CompletionStage接口;
  • 借助CompletionStage的方法可以实现链式调用;
  • 一个CompletetableFuture就代表了一个任务,可以用then,when等操作来防止阻塞和轮询isDone的现象出现;
  • 它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作;

CompletableFuture 结构如下:
在这里插入图片描述

CompletableFuture 实现了 Future 和 CompletionStage 两个接口。其中 CompletionStage 是Java8新增得一个接口,代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段。目前只有CompletableFuture以及其内部类进行了实现 :

  • 一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println())

  • 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发

三、方法介绍

下面我们来看 CompletableFuture 提供的方法,图源 : CompletableFuture基本使用
在这里插入图片描述

1. 任务创建

  • CompletableFuture#supplyAsync : 创建一个带有返回值的异步任务

    	// 使用默认的线程池
        public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
            return asyncSupplyStage(asyncPool, supplier);
        }
        // 使用自定义的线程池
        public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                           Executor executor) {
            return asyncSupplyStage(screenExecutor(executor), supplier);
        }
    
  • CompletableFuture#runAsync :创建一个没有返回值的异步任务

    	// 使用默认的线程池
        public static CompletableFuture<Void> runAsync(Runnable runnable) {
            return asyncRunStage(asyncPool, runnable);
        }
        // 使用自定义的线程池
        public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                       Executor executor) {
            return asyncRunStage(screenExecutor(executor), runnable);
        }
    

具体使用如下 :

     // supplyAsync有返回值
     CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "start");
   	 // runAsync 无返回值
     CompletableFuture.runAsync(() -> System.out.println("start"));

注 :

  1. CompletableFuture 默认的使用公共的 ForkJoinPool 线程池执行(默认线程数是 CPU 的核数);如果机器是单核的,则默认使用ThreadPerTaskExecutor,该类是一个内部类,每次执行execute都会创建一个新线程。由于我们在项目中无法控制 ForkJoinPool 在其他处的使用,因此使用默认的 ForkJoinPool 线程池可能会造成任务阻塞,此时我们可以使用自定义的线程池,不过需要注意自定义线程池有个队列满了的拒绝策略,如果采用丢弃策略的拒绝策略,并且allOf方法和get方法如果没有设置超时则会无限期的等待下去。
  2. CompletableFuture 提供的方法有一部分带有 Async 后缀 ,如 CompletableFuture#thenApply 和 CompletableFuture#thenApplyAsync,CompletableFuture#acceptEither 和 CompletableFuture#acceptEitherAsync 等,其区别在于,方法名带有 Async 修饰的其 function 是异步执行,否则为同步执行。

2. 函数回调

某个任务执行完成后执行的动作(即回调方法)可以理解为串行化一个任务接着一个执行。函数回调方法如下:

  1. CompletableFuture#thenApply 以及 CompletableFuture#thenApplyAsync:将上一个任务任务的执行结果,交给后面的Function执行,返回一个具有处理结果的Future对象,

    	// 同步执行
    	public <U> CompletableFuture<U> thenApply(
    	        Function<? super T,? extends U> fn) {
    	        return uniApplyStage(null, fn);
    	}
    	// 异步执行,使用默认的线程池
        public <U> CompletableFuture<U> thenApplyAsync(
            Function<? super T,? extends U> fn) {
            return uniApplyStage(asyncPool, fn);
        }
    	// 异步执行,使用入参指定的线程池
        public <U> CompletableFuture<U> thenApplyAsync(
            Function<? super T,? extends U> fn, Executor executor) {
            return uniApplyStage(screenExecutor(executor), fn);
        }
    

    下面几个方法同样存在 异步执行方法以及重载方法,实现基本相同,所以不再贴出

  2. CompletableFuture#thenAccept 以及 CompletableFuture#thenAcceptAsync:将上一个任务任务的执行结果(即方法返回值)作为入参传递到回调方法中,无返回值

        public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
            return uniAcceptStage(null, action);
        }
    
  3. CompletableFuture#thenRun 以及 CompletableFuture#thenRunAsync :无入参无返回值。不关心结果,只对结果执行Action

        public CompletableFuture<Void> thenRun(Runnable action) {
            return uniRunStage(null, action);
        }
    
  4. CompletableFuture#thenCompose 以及 CompletableFuture#thenComposeAsync :用来连接两个有依赖关系的任务

        public <U> CompletableFuture<U> thenCompose(
            Function<? super T, ? extends CompletionStage<U>> fn) {
            return uniComposeStage(null, fn);
        }
    

下面我们具体看下:

    public static void main(String[] args) throws Exception {
        // 创建异步任务
        CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "start");
        System.out.println("##############################");
        // thenApply : 将 supplyAsync 的处理结果作为入参,并将返回带有处理后结果的 CompletableFuture
        final CompletableFuture<String> thenApply = supplyAsync.thenApply(s -> s + " thenApply");
        // 输出 start thenApply
        System.out.println(thenApply.get());
        // thenAccept :将 supplyAsync 的处理结果作为入参,返回的 CompletableFuture 没有处理结果。
        // 输出 start thenAccept
        final CompletableFuture<Void> thenAccept = supplyAsync.thenAccept(s -> System.out.println(s + " thenAccept"));
        // thenRun : 无入参,返回的 CompletableFuture 没有处理结果。
        // 输出 thenRun
        final CompletableFuture<Void> thenRun = supplyAsync.thenRun(() -> System.out.println("thenRun"));
        // thenCompose : 将 supplyAsync 的处理结果作为入参, 内部经过 另一个 CompletableFuture 处理返回。
        // 一般来说内部的 CompletableFuture 依赖于外部的 CompletableFuture 的处理结果, 即这里的CompletableFuture.completedFuture(s) 是依赖于 supplyAsync 的
        // 输出 start thenCompose
        final CompletableFuture<String> thenCompose = supplyAsync.thenCompose(s -> CompletableFuture.completedFuture(s).thenApply(s1 -> s1 + " thenCompose"));
        System.out.println(thenCompose.get());
    }

3. 函数聚合

函数聚合分为两种情况:

  1. AND : 多个 CompletableFuture 任务都执行结束后,才会执行后续阶段。

  2. OR :多个 CompletableFuture 任务只要有一个执行结束,就会执行后续阶段。

3.1 AND

这里需要注意:

  1. 将多个CompletableFuture都执行完成以后,才会执行后阶段。
  2. 多个CompletableFuture并行执行,相互之间并没有先后依赖顺序。
  3. 多个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果,返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。

下面我们来看具体的方法举例:

  1. CompletableFuture#thenCombine 以及 thenCombineAsync

        public <U,V> CompletableFuture<V> thenCombine(
            CompletionStage<? extends U> other,
            BiFunction<? super T,? super U,? extends V> fn) {
            return biApplyStage(null, other, fn);
        }
    
  2. CompletableFuture#thenAcceptBoth 以及 CompletableFuture#thenAcceptBothAsync

        public <U> CompletableFuture<Void> thenAcceptBoth(
            CompletionStage<? extends U> other,
            BiConsumer<? super T, ? super U> action) {
            return biAcceptStage(null, other, action);
        }
    
  3. CompletableFuture#runAfterBoth 以及 CompletableFuture#runAfterBothAsync

        public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
                                                    Runnable action) {
            return biRunStage(null, other, action);
        }
    
  4. CompletableFuture#allOf

        public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
            return andTree(cfs, 0, cfs.length - 1);
        }
    

具体使用如下:

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> supplyStartAsync = CompletableFuture.supplyAsync(() -> "start");
        CompletableFuture<String> supplyEndAsync = CompletableFuture.supplyAsync(() -> "end");
        // AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,将执行结果作为返回值返回
        final CompletableFuture<String> thenCombine = supplyStartAsync.thenCombine(supplyEndAsync, (s, s2) -> "thenCombine " + s + " " + s2);
        // 输出 thenCombine start end
        System.out.println(thenCombine.get());
        // AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,无返回值
        // 输出 thenAcceptBoth start end
        final CompletableFuture<Void> thenAcceptBoth = supplyStartAsync.thenAcceptBoth(supplyEndAsync, (s, s2) -> System.out.println("thenAcceptBoth "+  s + " " + s2));
        // AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,无入参无返回值
        // 输出 runAfterBoth
        final CompletableFuture<Void> runAfterBoth = supplyStartAsync.runAfterBoth(supplyEndAsync, () -> System.out.println("runAfterBoth"));
        // 当存在多个 CompletableFuture 时, 可以通过 allOf 方法 : 当所有的 CompletableFuture 都执行结束后才会往下执行。
        CompletableFuture.allOf(supplyStartAsync, supplyEndAsync).join();
    }

3.2 OR

这里需要注意:

  1. 如果最先执行结束的任务发生了异常,则会将异常信息作为作为指定任务的执行结果,如果最先执行结束的任务并没有抛出异常,即使是后续任务出现了异常,也不会影响当前环节的结果作为指定任务的执行结果。

下面我们来看具体的方法实例:

  1. CompletableFuture#applyToEither 以及 CompletableFuture#applyToEitherAsync

        public <U> CompletableFuture<U> applyToEither(
            CompletionStage<? extends T> other, Function<? super T, U> fn) {
            return orApplyStage(null, other, fn);
        }
    
  2. CompletableFuture#acceptEither 以及 CompletableFuture#acceptEitherAsync

        public CompletableFuture<Void> acceptEither(
            CompletionStage<? extends T> other, Consumer<? super T> action) {
            return orAcceptStage(null, other, action);
        }
    
  3. CompletableFuture#runAfterEither 以及 CompletableFuture#runAfterEitherAsync

        public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                                      Runnable action) {
            return orRunStage(null, other, action);
        }
    
  4. CompletableFuture#anyOf

        public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
            return orTree(cfs, 0, cfs.length - 1);
        }
    

具体使用如下:

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> supplyStartAsync = CompletableFuture.supplyAsync(() -> "start")
        		// 这里要使用异步方法,同步方法使用 OR 规则没有意义,因为执行到 OR 规则时已经执行结束了。
                .thenApplyAsync(s -> {
                	// 为了演示 随机睡眠1-10毫秒,确保两个任务有先后顺序
                    sleep(RandomUtils.nextLong(1, 10));
                    return s;
                });
        CompletableFuture<String> supplyEndAsync = CompletableFuture.supplyAsync(() -> "end")
                .thenApplyAsync(s -> {
                    sleep(RandomUtils.nextLong(1, 10));
                    return s;
                });
        // supplyStartAsync 和 supplyEndAsync 谁先执行结束用谁的作为 function 入参,因此这里的输出可能是 applyToEither start 或 applyToEither end
        final CompletableFuture<String> applyToEither = supplyStartAsync.applyToEither(supplyEndAsync, s -> "applyToEither " + s);
        // 输出 applyToEither start 或 applyToEither end
        System.out.println(applyToEither.get());
        // supplyStartAsync 和 supplyEndAsync 谁先执行结束用谁的作为 function 入参
        // 输出 acceptEither start 或 acceptEither end
        final CompletableFuture<Void> acceptEither = supplyStartAsync.acceptEither(supplyEndAsync, (s) -> System.out.println("acceptEither " + s));
        // supplyStartAsync 和 supplyEndAsync 任意一个执行结束即会执行 Function
        // 输出 runAfterEither
        final CompletableFuture<Void> runAfterEither = supplyStartAsync.runAfterEither(supplyEndAsync, () -> System.out.println("runAfterEither"));
        // 当存在多个 CompletableFuture 时, anyOf 指定的 CompletableFuture 中有一个完成就会往下继续执行
        CompletableFuture.anyOf(supplyStartAsync, supplyEndAsync).join();
	}

4. 结果处理

  1. CompletableFuture#handle 以及 CompletableFuture#handleAsync

        public <U> CompletableFuture<U> handle(
            BiFunction<? super T, Throwable, ? extends U> fn) {
            return uniHandleStage(null, fn);
        }
    
  2. CompletableFuture#exceptionally

        public CompletableFuture<T> exceptionally(
            Function<Throwable, ? extends T> fn) {
            return uniExceptionallyStage(fn);
        }
    
  3. CompletableFuture#whenComplete 以及 CompletableFuture#whenCompleteAsync

        public CompletableFuture<T> whenComplete(
            BiConsumer<? super T, ? super Throwable> action) {
            return uniWhenCompleteStage(null, action);
        }
    

具体使用如下:

public static void main(String[] args) throws Exception {
    final String result = CompletableFuture.supplyAsync(() -> "end")
    		// 1. 抛出异常
            .thenApplyAsync(s -> {
                if (StringUtils.isNotBlank(s)) {
                    throw new RuntimeException(s);
                }
                return s;
            })
            // 2. 前面的任务执行结束后调用,我们这里前一步抛出了异常,所以这里 S 为空, throwable有值
            .whenComplete(new BiConsumer<String, Throwable>() {
            	// s : 正常执行的结果
            	// throwable : 非正常执行抛出的异常
                @Override
                public void accept(String s, Throwable throwable) {
                    System.out.println("whenComplete s = " + s);
                    System.out.println("whenComplete throwable = " + throwable);
                }
            })
            // 异常处理,前面的步骤有异常抛出时才会执行,这里可以对异常进行处理并返回一个新的结果。
            .exceptionally(new Function<Throwable, String>() {
                @Override
                public String apply(Throwable throwable) {
                    System.out.println("exceptionally throwable = " + throwable);
                    return "throwable";
                }
            })
            // 对 前一步的结果做处理
            .thenApply(s -> s + " theApply")
            // 处理结果,这里 s 已经在 exceptionally 被转换,所以 s 不为空,throwable 为空
            .handle(new BiFunction<String, Throwable, String>() {
                // s : 正常执行的结果
            	// throwable : 非正常执行抛出的异常
                @Override
                public String apply(String s, Throwable throwable) {
                    System.out.println("handle s = " + s);
                    System.out.println("handle throwable = " + throwable);
                    return s;
                }
            }).join();
// 最终输出 :throwable theApply
    System.out.println(result);
}
    

综上输出内容如下:

whenComplete s = null
whenComplete throwable = java.util.concurrent.CompletionException: java.lang.RuntimeException: end
exceptionally throwable = java.util.concurrent.CompletionException: java.lang.RuntimeException: end
handle s = throwable theApply
handle throwable = null
throwable theApply

5. 结果获取

结果获取方法有下面三种,其使用比较简单,这里不再举例。

  1. CompletableFuture#get : 如有必要,等待此未来完成,然后返回其结果。
  2. CompletableFuture#getNow : 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
  3. CompletableFuture#join : 完成时返回结果值,如果异常完成则抛出(未经检查的)异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture 所涉及的计算引发异常,则此方法将引发(未经检查的) CompletionException ,并将底层异常作为其原因。

本文仅仅做个简单使用的介绍,在Java基础 : CompletableFuture② 代码浅析中对CompletableFuture 的实现做力所能及的学习。


以上:内容部分参考
https://blog.csdn.net/ChenShiAi/article/details/123913374
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猫吻鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值