CompletableFuture使用详解

        简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多
时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Future 实现,是非常麻烦的。
        CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是,
CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过
CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。

应用场景:

描述依赖关系:

thenApply() 把前面异步任务的结果,交给后面的Function
thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回


描述and聚合关系:

thenCombine:任务合并,有返回值
thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值。
runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)


描述or聚合关系:

applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值。
acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值。
runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)


并行执行:

CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行

创建异步操作:

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

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor
executor)

这四个方法区别在于:

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

thenApply

thenApply 接收一个函数作为参数,使用该函数处理上一个CompletableFuture 调用的结果,并返回一个具有处理结果的Future对象。

thenCompose

thenCompose 的参数为一个返回 CompletableFuture 实例的函数,该函数的参数是先前计算步骤的结果。

thenAccept

通过观察该系列函数的参数类型可知,它们是函数式接口Consumer,这个接口只有输入,没有返回值。

thenAcceptBoth

thenAcceptBoth 函数的作用是,当两个 CompletionStage 都正常完成计算的时候,就会执行提供的action消费两个异步的结果。

thenRun

thenRun 也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun 会在上一阶段
CompletableFuture 计算完成的时候执行一个Runnable,Runnable并不使用该CompletableFuture 计算的结果。

thenCombine

thenCombine 方法,合并两个线程任务的结果,并进一步处理。

applyToEither:

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。

acceptEither:

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。

runAfterEither:

两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。

runAfterBoth:

两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。

anyOf:

anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个
CompletableFuture。

allOf:

allOf方法用来实现多 CompletableFuture 的同时返回。

CompletableFuture实战:

<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$等标记对象减少内存分配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值