CompletableFuture常用方法(基于Java8)

简介

从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

CompletableFuture类实现了CompletionStage和Future接口,所以你还是可以像以前一样通过阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。

静态方法

异步执行一个线程

CompletableFuture提供了四个静态方法来创建一个异步操作:

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

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
方法备注
supplyAsync以Supplier函数式接口类型为参数,返回结果类型为U,类似于ExecutorService submit(Callable task) 方法;Supplier接口的 get()是有返回值的(会阻塞)。
runAsync以Runnable函数式接口类型为参数,没有返回结果,类似于ExecutorService submit(Runnable task) 方法。
  • 使用没有指定Executor的方法时,内部使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。
  • 默认情况下CompletableFuture会使用公共的ForkJoinPool线程池,这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置ForkJoinPool线程池的线程数)。如果所有CompletableFuture共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。
  • 所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰。

线程池参考资料:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

线程池创建建议使用如下语句:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

示例:

    @DisplayName("supplyAsync")
    @Test
    public void supplyAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("supplyAsync方法执行,执行时间:" + LocalDateTime.now());
            return "SUCCESS";
        }, threadPool);
        System.out.println("main thread:" + LocalDateTime.now());
    }

    @Test
    @DisplayName("runAsync")
    public void runAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture.runAsync(() -> {
            System.out.println("runAsync方法执行,执行时间:" + LocalDateTime.now());
        }, threadPool);
        System.out.println("main thread:" + LocalDateTime.now());
    }

执行结果:

main thread:2022-04-19T11:20:30.680
main thread:2022-04-19T11:20:30.683
runAsync方法执行,执行时间:2022-04-19T11:20:30.684
main thread:2022-04-19T11:20:30.691
main thread:2022-04-19T11:20:30.692
supplyAsync方法执行,执行时间:2022-04-19T11:20:30.692
并行执行
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
方法备注
allOf当所有给定的CompletableFuture完成时,返回一个新的CompletableFuture;如果有一个任务异常终止,则get时会抛出异常;如果都是正常运行,则get时返回null。
anyOf当任何一个给定的CompletablFuture完成时,返回一个新的CompletableFuture;只有一个任务执行完成,无论是正常执行还是异常执行,都会执行,调用get的结果就是已执行完成的任务的执行结果。
  • allOf返回的CompletableFuture是多个任务都执行完成后才会执行,只要有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常;如果都是正常执行,则get返回null。
  • anyOf返回的CompletableFuture是多个任务只要其中一个执行完成就会执行,其get返回的是已经执行完成的任务的执行结果,如果该任务执行异常,则抛出异常。
    /**
     * 以乘坐公交车为例
     */
    @Test
    @DisplayName("anyOf")
    public void anyOf_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("222路公交还有 " + number + " 分钟到站!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("222路公交到站,到站时间:" + LocalDateTime.now());
            return "222";
        }, threadPool);
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("111路公交还有 " + number + " 分钟到站!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("111路公交到站,到站时间:" + LocalDateTime.now());
            return 111;
        }, threadPool);
        CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(completableFuture1, completableFuture2);
        try {
            Object object = completableFuture.get();
            System.out.println(object + " 路公交车到站,乘车");
        } catch (InterruptedException exception) {
            System.out.println("车抛锚了");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("车到站抛锚了");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

正常场景:
main thread:2022-04-19T14:11:24.192
222路公交还有 2 分钟到站!
111路公交还有 1 分钟到站!
111路公交到站,到站时间:2022-04-19T14:11:25.203
111 路公交车到站,乘车
main thread:2022-04-19T14:11:25.204

异常场景:
main thread:2022-04-19T14:09:30.438
222路公交还有 2 分钟到站!
111路公交还有 0 分钟到站!
车到站抛锚了
14:09:30.445 [main] ERROR com.huawei.it.CompletableFutureDemo - java.lang.ArithmeticException: / by zero
    /**
     * 以饭店上餐为例
     */
    @Test
    @DisplayName("allOf")
    public void allOf_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("鱼香肉丝还有 " + number + " 分钟上菜!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("鱼香肉丝上菜时间:" + LocalDateTime.now());
            return 100;
        }, threadPool);
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("椰子鸡还有 " + number + " 分钟上菜!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("椰子鸡上菜时间:" + LocalDateTime.now());
            return "200";
        }, threadPool);
        CompletableFuture<Void> completableFuture3 = CompletableFuture.runAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("米饭还有 " + number + " 分钟盛好!");
            sleepSeconds(number);
            System.out.println("米饭:" + LocalDateTime.now());
            double temp = 100 / number;
        }, threadPool);
        CompletableFuture<Void> completableFuture = CompletableFuture.allOf(completableFuture1, completableFuture2, completableFuture3);
        try {
            // 注意这行代码的作用
            completableFuture.get();
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

正常场景:
main thread:2022-04-20T16:26:23.261
鱼香肉丝还有 3 分钟上菜!
椰子鸡还有 1 分钟上菜!
米饭还有 4 分钟盛好!
椰子鸡上菜时间:2022-04-20T16:26:24.280
鱼香肉丝上菜时间:2022-04-20T16:26:26.266
米饭:2022-04-20T16:26:27.277
main thread:2022-04-20T16:26:27.277

异常场景:
main thread:2022-04-20T16:29:23.933
鱼香肉丝还有 2 分钟上菜!
椰子鸡还有 4 分钟上菜!
米饭还有 0 分钟盛好!
米饭:2022-04-20T16:29:23.937
鱼香肉丝上菜时间:2022-04-20T16:29:25.941
椰子鸡上菜时间:2022-04-20T16:29:27.939
~~~ExecutionException异常~~~
16:29:27.944 [main] ERROR com.huawei.it.CompletableFutureDemo - java.lang.ArithmeticException: / by zero

实例方法

聚合关系(OR)
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor);

public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn, Executor executor);

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor);

注意方法中Async关键字表示异步执行。注意泛型参数调用。

方法备注
acceptEitherAsync两个任务哪个执行的快,就消费哪一个结果,无返回值
applyToEitherAsync两个任务哪个执行的快,就使用哪一个结果,有返回值
runAfterEitherAsync任意一个任务执行完成,进行下一步操作(Runnable类型任务)

三个方法都是将两个CompletableFuture组合起来,只要其中一个执行完了就会执行某个任务,其区别在于applyToEither会将已经执行完成的任务的执行结果作为方法入参,并有返回值;acceptEither同样将已经执行完成的任务的执行结果作为方法入参,但是没有返回值;runAfterEither没有方法入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。

    /**
     * 方案选择
     */
    @Test
    @DisplayName("acceptEitherAsync")
    public void acceptEitherAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成" + LocalDateTime.now());
            return "方案一";
        }, threadPool);
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成" + LocalDateTime.now());
            return "方案二";
        }, threadPool);
        CompletableFuture<Void> completableFuture = completableFuture1.acceptEitherAsync(completableFuture2, (String string) -> {
            System.out.println("最后执行的是:" + string);
        }, threadPool);
        try {
            // 注意这行代码的作用
            completableFuture.get();
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

正常场景:
main thread:2022-04-21T15:50:38.042
方案一执行需要: 4 秒!
方案二执行需要: 3 秒!
方案二执行完成2022-04-21T15:50:41.055
最后执行的是:方案二
main thread:2022-04-21T15:50:41.055

异常场景:
main thread:2022-04-21T15:51:52.985
方案二执行需要: 0 秒!
方案一执行需要: 1 秒!
~~~ExecutionException异常~~~
15:51:52.992 [main] ERROR com.huawei.it.CompletableFutureDemo - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T15:51:53
    @Test
    @DisplayName("applyToEitherAsync")
    public void applyToEitherAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        // 返回值泛型类型为Object
        CompletableFuture<Object> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成" + LocalDateTime.now());
            return 100 * (number + 1);
        }, threadPool);
        // 返回值泛型类型为String
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成" + LocalDateTime.now());
            return "方案二";
        }, threadPool);
        // completableFuture1.applyToEitherAsync(completableFuture2...)是可以的,反过来报错:无法推断类型变量 U,实际参数列表和形式参数列表长度不同
        // 返回值泛型类型为Double
        CompletableFuture<Double> completableFuture = completableFuture1.applyToEitherAsync(completableFuture2, (object) -> {
            System.out.println("最终的结果是:" + object);
            return 3.1415;
        }, threadPool);
        try {
            // 注意这行代码的作用
            Double result = completableFuture.get();
            System.out.println("结果为:" + result);
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

正常场景:
main thread:2022-04-21T16:15:08.434
方案一执行需要: 1 秒!
方案二执行需要: 3 秒!
方案一执行完成2022-04-21T16:15:09.440
最终的结果是:200
结果为:3.1415
main thread:2022-04-21T16:15:09.444

异常场景:
main thread:2022-04-21T16:15:34.939
方案一执行需要: 3 秒!
方案二执行需要: 0 秒!
~~~ExecutionException异常~~~
16:15:34.946 [main] ERROR com.huawei.it.CompletableFutureDemo - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T16:15:34.953
    @Test
    @DisplayName("runAfterEitherAsync")
    public void runAfterEitherAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成" + LocalDateTime.now());
            return "方案一";
        }, threadPool);
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成" + LocalDateTime.now());
            return "方案二";
        }, threadPool);
        CompletableFuture<Void> completableFuture = completableFuture1.runAfterEitherAsync(completableFuture2, () -> {
            System.out.println("执行此部分代码,没有参数,没有返回值");
        }, threadPool);
        try {
            // 注意这行代码的作用
            completableFuture.get();
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

正常场景:
main thread:2022-04-21T18:03:33.587
方案一执行需要: 3 秒!
方案二执行需要: 3 秒!
方案一执行完成2022-04-21T18:03:36.597
方案二执行完成2022-04-21T18:03:36.597
执行此部分代码,没有参数,没有返回值
main thread:2022-04-21T18:03:36.597

异常场景:
main thread:2022-04-21T18:17:10.950
方案一执行需要: 0 秒!
方案二执行需要: 1 秒!
~~~ExecutionException异常~~~
18:17:10.958 [main] ERROR com.huawei.it.CompletableFutureDemo - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T18:17:10.967
集合关系(AND)
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor);

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor);

public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor);
方法备注
thenCombineAsync将两个任务的执行结果作为方法入参传递到指定方法中,并进一步处理,该方法有返回值。
thenAcceptBothAsync将两个任务的执行结果作为方法入参传递到指定方法中,并进一步处理,但是无返回值
runAfterBothAsync两个任务都执行完成后,执行下一步操作(Runnable类型任务)

三个方法都是将两个CompletableFuture组合起来,只有这两个都正常执行完了才会执行某个任务,区别在于,thenCombine会将两个任务的执行结果作为方法入参传递到指定方法中,且该方法有返回值;thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。

    @Test
    @DisplayName("thenCombineAsync")
    public void thenCombineAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成,返回String类型!" + LocalDateTime.now());
            return "方案一";
        }, threadPool);
        CompletableFuture<Long> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成,返回Long类型!" + LocalDateTime.now());
            return 100L;
        }, threadPool);
        // 注意参数顺序,将两个任务的执行结果作为方法入参传递到指定方法中
        CompletableFuture<Double> completableFuture = completableFuture1.thenCombineAsync(completableFuture2, (String string, Long number) -> {
            System.out.println("方案一返回结果:" + string);
            System.out.println("方案二返回结果:" + number);
            return 3.1415926;
        }, threadPool);
        try {
            // 注意这行代码的作用
            Double result = completableFuture.get();
            System.out.println("最终结果为:" + result);
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

main thread:2022-04-21T22:13:24.378
方案一执行需要: 1 秒!
方案二执行需要: 3 秒!
方案一执行完成,返回String类型!2022-04-21T22:13:25.384
方案二执行完成,返回Long类型!2022-04-21T22:13:27.386
方案一返回结果:方案一
方案二返回结果:100
最终结果为:3.1415926
main thread:2022-04-21T22:13:27.390

main thread:2022-04-21T22:15:12.577
方案一执行需要: 0 秒!
方案二执行需要: 1 秒!
方案二执行完成,返回Long类型!2022-04-21T22:15:13.580
~~~ExecutionException异常~~~
22:15:13.585 [main] ERROR com.lwy.it.CompletableFutureDemoTest - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T22:15:13.595
    @Test
    @DisplayName("thenAcceptBothAsync")
    public void thenAcceptBothAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成,返回String类型!" + LocalDateTime.now());
            return "方案一";
        }, threadPool);
        CompletableFuture<Long> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成,返回Long类型!" + LocalDateTime.now());
            return 100L;
        }, threadPool);
        // 注意参数顺序
        CompletableFuture<Void> completableFuture = completableFuture1.thenAcceptBothAsync(completableFuture2, (String string, Long number) -> {
            System.out.println("方案一返回结果:" + string + "~~~~" + LocalDateTime.now());
            System.out.println("方案二返回结果:" + number + "~~~~" + LocalDateTime.now());
        }, threadPool);
        try {
            // 注意这行代码的作用
            completableFuture.get();
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

main thread:2022-04-21T22:22:03.933
方案一执行需要: 1 秒!
方案二执行需要: 4 秒!
方案一执行完成,返回String类型!2022-04-21T22:22:04.938
方案二执行完成,返回Long类型!2022-04-21T22:22:07.941
方案一返回结果:方案一~~~~2022-04-21T22:22:07.942
方案二返回结果:100~~~~2022-04-21T22:22:07.942
main thread:2022-04-21T22:22:07.942

main thread:2022-04-21T22:20:51.501
方案一执行需要: 0 秒!
方案二执行需要: 3 秒!
方案二执行完成,返回Long类型!2022-04-21T22:20:54.510
~~~ExecutionException异常~~~
22:20:54.517 [main] ERROR com.lwy.it.CompletableFutureDemoTest - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T22:20:54.527
    @Test
    @DisplayName("runAfterBothAsync")
    public void runAfterBothAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案一执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案一执行完成,返回String类型!" + LocalDateTime.now());
            return "方案";
        }, threadPool);
        CompletableFuture<Long> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("方案二执行需要: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("方案二执行完成,返回Long类型!" + LocalDateTime.now());
            return 100L;
        }, threadPool);
        // 注意参数顺序
        CompletableFuture<Void> completableFuture = completableFuture1.runAfterBothAsync(completableFuture2, () -> {
            System.out.println("执行一系列无返回值操作");
        }, threadPool);
        try {
            // 注意这行代码的作用
            completableFuture.get();
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

main thread:2022-04-21T22:25:35.456
方案一执行需要: 3 秒!
方案二执行需要: 2 秒!
方案二执行完成,返回Long类型!2022-04-21T22:25:37.480
方案一执行完成,返回String类型!2022-04-21T22:25:38.481
执行一系列无返回值操作
main thread:2022-04-21T22:25:38.482

main thread:2022-04-21T22:26:15.878
方案一执行需要: 2 秒!
方案二执行需要: 0 秒!
方案一执行完成,返回String类型!2022-04-21T22:26:17.884
~~~ExecutionException异常~~~
22:26:17.891 [main] ERROR com.lwy.it.CompletableFutureDemoTest - java.lang.ArithmeticException: / by zero
...
main thread:2022-04-21T22:26:17.903
依赖关系
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor);
方法备注
thenApplyAsync把前面任务的执行结果,交给后面的函数的参数,使用该函数处理上一个CompletableFuture调用的结果,并返回一个具有处理结果的CompletableFuture对象。表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中。
thenComposeAsync用来连接两个有依赖关系的任务,结果由第二个任务返回。参数为一个返回CompletableFuture实例的函数,该函数的参数是先前计算步骤的结果。
  • thenApply:转换的是泛型中的类型,返回的是同一个CompletableFuture
  • thenCompose:将内部的CompletableFuture调用展开来并使用上一个CompletableFutre调用的结果在下一步的CompletableFuture调用中进行运算,是生成一个新的CompletableFuture。
    @Test
    @DisplayName("thenApplyAsync")
    public void thenApplyAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("此处暂停: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("此步骤返回Long类型 100 ,时间为:" + LocalDateTime.now());
            return 100L;
        }, threadPool).thenApplyAsync((Long parameter) -> { // 结果转换,将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。
            int number = new Random().nextInt(5);
            System.out.println("此处暂停: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("此步骤返回String类型 SUCCESS ,时间为:" + LocalDateTime.now());
            return "SUCCESS!";
        }, threadPool);
        try {
            // 注意这行代码的作用
            String result = completableFuture.get();
            System.out.println("最终结果为:" + result);
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

main thread:2022-04-21T23:02:25.754
此处暂停: 4 秒!
此步骤返回Long类型 100 ,时间为:2022-04-21T23:02:29.761
此处暂停: 3 秒!
此步骤返回String类型 SUCCESS ,时间为:2022-04-21T23:02:32.763
最终结果为:SUCCESS!
main thread:2022-04-21T23:02:32.763
    @Test
    @DisplayName("thenComposeAsync")
    public void thenComposeAsync_test() {
        System.out.println("main thread:" + LocalDateTime.now());
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            System.out.println("第一次暂停: " + number + " 秒!");
            sleepSeconds(number);
            double temp = 100 / number;
            System.out.println("此步骤返回Long类型 100 ,时间为:" + LocalDateTime.now());
            return 100L;
        }, threadPool).thenComposeAsync((Long parameter) -> {
            return CompletableFuture.supplyAsync(() -> {
                int number = new Random().nextInt(5);
                System.out.println("第二次暂停: " + number + " 秒!");
                sleepSeconds(number);
                double temp = 100 / number;
                System.out.println("此步骤返回String类型 SUCCESS ,时间为:" + LocalDateTime.now());
                return "SUCCESS!";
            }, threadPool);
        }, threadPool);
        try {
            // 注意这行代码的作用
            String result = completableFuture.get();
            System.out.println("最终结果为:" + result);
        } catch (InterruptedException exception) {
            System.out.println("~~~InterruptedException异常~~~");
            log.error(exception.getMessage(), exception);
        } catch (ExecutionException exception) {
            System.out.println("~~~ExecutionException异常~~~");
            log.error(exception.getMessage(), exception);
        }
        System.out.println("main thread:" + LocalDateTime.now());
    }

结果为:

main thread:2022-04-21T23:04:53.277
第一次暂停: 4 秒!
此步骤返回Long类型 100 ,时间为:2022-04-21T23:04:57.281
第二次暂停: 1 秒!
此步骤返回String类型 SUCCESS ,时间为:2022-04-21T23:04:58.286
最终结果为:SUCCESS!
main thread:2022-04-21T23:04:58.287
结果处理
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor);

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
方法备注
whenCompleteAsync当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常。
exceptionally指定某个任务执行异常时执行的回调方法,会将抛出异常作为参数传递到回调方法中,如果该任务正常执行则会exceptionally方法返回的CompletionStage的result就是该任务正常执行的结果

CompletableFuture常用方法总结:

分类方法返回值备注
异步执行runAsyncVoid
异步执行supplyAsync
两个线程依次执行thenApply
两个线程依次执行thenAcceptVoid
两个线程依次执行thenRunVoid
两个线程依次执行whenComplete
两个线程依次执行exceptionally
两个线程依次执行handle
等待两个线程都执行完thenCombine
等待两个线程都执行完thenAcceptBothVoid
等待两个线程都执行完runAfterBothVoid
两个线程任一执行完applyToEither
两个线程任一执行完acceptToEitherVoid
两个线程任一执行完runAfterEitherVoid
多个线程等待anyOfObject多个线程任何一个线程执行完立即返回
多个线程等待allOfVoid多个线程全部执行完返回

参考文档:

https://blog.csdn.net/sermonlizhi/article/details/123356877

https://blog.csdn.net/qq_31865983/article/details/106137777

https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值