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异步

<think>嗯,用户想了解CompletableFuture的原理和详细实现,特别是Java异步编程方面的深入分析。首先,我需要回顾一下之前提供的引用内容,看看有哪些信息可以利用。引用[1]提到CompletableFutureJava 8引入的,用于异步编程,属于java.util.concurrent包,相比传统Future更灵活,支持链式调用、任务合并和异常处理。引用[2]强调了它的高效异步模式和并发编程的简洁性。引用[3]指出CompletableFuture有很多方法和内部类,需要分步分析。引用[4]提到了Java并发的发展,包括ForkJoin框架等。 用户的问题是关于原理和实现的详细解析,所以需要深入讨论内部机制,比如如何实现异步任务的管理、回调链的处理、线程池的使用,以及如何组合多个任务。可能还需要涉及CompletableFuture的状态机、依赖关系处理、异常传播机制等。 首先,CompletableFuture基于ForkJoinPool,这点应该在实现原理部分提到。然后,它的核心结构可能涉及到每个阶段(stage)作为一个节点,形成链表或栈结构来处理依赖。状态管理方面,可能用volatile变量来保存结果和状态变化,确保可见性。 另外,需要解释CompletableFuture如何通过thenApply、thenAccept等方法链式组合任务,这些方法如何注册回调,并在前一个阶段完成时触发后续操作。这里可能涉及CompletionStage接口的实现,以及各种不同类型的Completion子类,比如UniApply、BiApply等,处理不同的依赖情况。 异常处理也是一个重点,CompletableFuture如何捕获异常并传递给后续的exceptionally或handle方法。需要说明当某个阶段出现异常时,后续依赖的阶段如何被触发,并处理异常结果。 线程池的集成部分,用户可能想知道默认使用的线程池,以及如何自定义。比如,默认使用ForkJoinPool.commonPool(),但也可以指定自定义的Executor。在实现中,如何将任务提交到线程池,并在任务完成后执行回调。 还有,组合多个CompletableFuture的方法,如allOf、anyOf,这些是如何实现的,可能涉及内部计数器或条件判断,等待所有或任意一个Future完成。 另外,性能优化方面,CompletableFuture如何减少线程阻塞,通过非阻塞的回调机制提高效率。对比传统的Future.get(),CompletableFuture通过回调通知机制避免了阻塞等待。 可能用户还想了解与响应式编程的区别,或者与其他异步框架如RxJava的不同之处,但根据问题,重点应放在CompletableFuture本身的原理和实现细节上。 需要确保回答结构清晰,分点讲解核心原理、状态管理、依赖处理、线程池机制、组合任务和异常处理等部分。同时,结合代码示例说明,比如创建异步任务的方法,链式调用的例子,以及异常处理的示例。 最后,生成相关问题时要围绕用户可能的进一步疑问,比如与传统的Future对比、如何选择线程池、实际应用场景、性能优化方法、异常处理最佳实践,以及和响应式编程的结合等。</think># CompletableFuture原理与实现深度解析 ## 一、核心实现原理 CompletableFuture基于观察者模式构建异步任务链,内部采用$stage$链表结构管理任务依赖关系。每个阶段通过$UniCompletion$/$BiCompletion$等内部类实现回调注册,采用无锁CAS操作保证线程安全[^3]。 状态机实现包含三个关键变量: $$ \begin{cases} result & \text{存储计算结果/异常对象} \\ stack & \text{等待触发的回调函数栈} \\ nest & \text{防止递归调用计数器} \end{cases} $$ ## 二、任务执行流程 1. **异步启动**:通过`supplyAsync()`创建任务时,默认使用$ForkJoinPool.commonPool()$[^4] ```java CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { // 异步任务逻辑 return "result"; }); ``` 2. **依赖传播**:每个`thenApply()`调用生成新的$UniApply$节点并压入前驱的栈顶 3. **完成触发**:当前任务完成后,通过`postComplete()`方法反向遍历栈节点触发后续操作 ## 三、线程池集成机制 | 配置方式 | 实现类 | 适用场景 | |-------------------|-------------------------|-----------------------| | 默认线程池 | ForkJoinPool.commonPool | CPU密集型任务 | | 自定义线程池 | ThreadPoolExecutor | IO密集型任务 | | 直接执行 | Runnable::run | 快速回调(同线程执行) | ## 四、组合任务实现 1. **AND聚合**:`allOf()`通过$BiRelay$合并多个future结果 2. **OR聚合**:`anyOf()`使用$AtomicReference$竞争设置首个完成结果 3. **嵌套组合**:`thenCompose()`通过$UniCompose$实现future级联 ## 五、异常处理机制 1. 异常传播路径:通过$AltResult$包装异常对象沿任务链传递 2. 恢复机制:`exceptionally()`注册的恢复函数可返回替代值 3. 全局捕获:`handle()`方法同时接收结果和异常参数 ```java CompletableFuture.supplyAsync(() -> { if (new Random().nextBoolean()) throw new RuntimeException("模拟异常"); return "data"; }).exceptionally(ex -> { System.out.println("捕获异常: " + ex.getMessage()); return "default"; }); ``` ## 六、性能优化设计 1. 延迟提交:使用$LazyExecutor$减少线程切换开销 2. 栈优化:采用Treiber栈结构提升并发压栈性能[^2] 3. 对象复用:内部$ASYNC$/$NIL$等标记对象减少内存分配
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值