CompletableFuture 使用方法及原理

一、背景

目前一些小伙伴还在使用线程池 + Future的方式,不太了解CompletableFuture,也不习惯于使用更加优雅的CompletableFuture。因此,主要是介绍CompletableFuture, 后续在项目中多多使用CompletableFuture, 提升代码的优雅度,增加开发效率。

二、相关知识

2.1 基本介绍

CompletableFuture是Java 8引入的一个异步编程工具,它实现了Future接口,可以用于表示一个尚未完成的操作。与Java中的Future不同,CompletableFuture提供了更丰富的方法来处理异步任务的结果和异常。CompletableFuture 解决了 Future 的一些问题,同时提供了一些更加强大的功能:

  1. 多个异步计算结果的组合:Future 之间是相互独立的,无法将多个 Future 组合成一个复杂异步计算。CompletableFuture 提供了一些方法,如 thenApply 、thenCompose 等,可以方便地将多个异步计算组合起来;
  2. 回调函数:Future 只能通过 Future.get() 方法来阻塞等待异步计算结果,这种方式会影响程序性能。CompletableFuture 可以通过 thenApply 、thenAccept 等方法,在异步计算完成后执行回调函数,提高了程序的响应性能;
  3. 异常处理:CompletableFuture提供了丰富的异常处理方法,可以在异步任务发生异常时进行捕获和处理;
  4. 链式调用:CompletableFuture支持链式调用,可以将多个异步操作组合在一起,形成一个异步任务链。这使得代码更加简洁易读。

2.2 各种场景

任务类型解决方案
简单并行任务线程池 + FutureTask
聚合任务CompletableFuture
批量并行任务CompletionService
分治任务Fork/Join

三、CompletableFuture的具体使用

3.1 从顶层认识CompletableFuture

从任务关系的角度来认识CompletableFuture:
在这里插入图片描述
从阶段维度认识CompletableFuture:
在这里插入图片描述

3.2 CompletableFuture提供的具体实现

3.2.1 创建CompletableFuture

提供了四种方法,如下所示:

// 使用默认线程池
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    // 执行任务
    System.out.println("任务1在默认线程池中执行");
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    // 执行任务
    System.out.println("任务2在默认线程池中执行");
    return "任务2执行结果";
});

// 指定线程池
Executor executor = Executors.newFixedThreadPool(2);
CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
    // 执行任务
    System.out.println("任务3在指定线程池中执行");
}, executor);

CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> {
    // 执行任务
    System.out.println("任务4在指定线程池中执行");
    return "任务4执行结果";
}, executor);

3.2.2 执行CompletableFuture

从串行角度看:主要是 thenApply、thenAccept、thenRun 和 thenCompose 这四个系列的接口

CompletionStage<R> thenApply(fn);
CompletionStage<R> thenApplyAsync(fn);
CompletionStage<Void> thenAccept(consumer);
CompletionStage<Void> thenAcceptAsync(consumer);
CompletionStage<Void> thenRun(action);
CompletionStage<Void> thenRunAsync(action);
CompletionStage<R> thenCompose(fn);
CompletionStage<R> thenComposeAsync(fn);

从并行的And角度看:主要是 thenCombine、thenAcceptBoth 和 runAfterBoth 系列的接口

CompletionStage<R> thenCombine(other, fn);
CompletionStage<R> thenCombineAsync(other, fn);
CompletionStage<Void> thenAcceptBoth(other, consumer);
CompletionStage<Void> thenAcceptBothAsync(other, consumer);
CompletionStage<Void> runAfterBoth(other, action);
CompletionStage<Void> runAfterBothAsync(other, action);

从并行的OR角度看:主要是 applyToEither、acceptEither 和 runAfterEither 系列的接口

CompletionStage applyToEither(other, fn);
CompletionStage applyToEitherAsync(other, fn);
CompletionStage acceptEither(other, consumer);
CompletionStage acceptEitherAsync(other, consumer);
CompletionStage runAfterEither(other, action);
CompletionStage runAfterEitherAsync(other, action);

3.2.3 执行结束处理

主要是对正常结果的处理和异常结果的处理。上述提供的方法不允许抛出可检查型异常,但无法限制抛出运行时异常。CompletableFuture提供了优雅的方案来处理该问题:

CompletionStage exceptionally(fn);
CompletionStage<R> whenComplete(consumer);
CompletionStage<R> whenCompleteAsync(consumer);
CompletionStage<R> handle(fn);
CompletionStage<R> handleAsync(fn);

四、CompletableFuture原理

4.1 核心的类

CompletableFuture类定义:

public class CompletableFuture<T>
extends Object
implements java.util.concurrent.Future<T>, java.util.concurrent.CompletionStage<T> {
	volatile Object result;       // Either the result or boxed AltResult
	volatile Completion stack;    // Top of Treiber stack of dependent actions
}

一个是Completion对象stack,这是一个CAS实现的无锁并发栈,每个链式调用的任务会被压入这个栈。另一个是Object对象result,这是当前CompletableFuture的结果

CompletableStage接口:CompletableStage用来表示异步过程中的一个阶段,它可以在另一个CompletableStage完成时做一些操作或计算,此接口中定义了一些基本的行为,通过这些行为组合可以简洁的描述非常复杂的任务。查看官方注释文档:CompletionStage是一个可能执行异步计算的“阶段”,这个阶段会在另一个CompletionStage完成时调用去执行动作或者计算,一个CompletionStage会以正常完成或者中断的形式“完成”,并且它的“完成”会触发其他依赖的CompletionStage。CompletionStage 接口的方法一般都返回新的CompletionStage,因此构成了链式的调用。

public interface CompletionStage<T> {
    public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
    public <U> CompletionStage<U> thenApplyAsync
        (Function<? super T,? extends U> fn);
}

Comletion类定义:管理当前CompletableFuture的关系以及执行器

/** A Completion with a source, dependent, and executor. */
@SuppressWarnings("serial")
abstract static class UniCompletion<T,V> extends Completion {
    Executor executor;                 // executor to use (null if none)
    CompletableFuture<V> dep;          // the dependent to complete 真正控制当前执行的流程
    CompletableFuture<T> src;          // source for action 触发它执行的源头
}

/* ------------- Base Completion classes and operations -------------- */

@SuppressWarnings("serial")
abstract static class Completion extends ForkJoinTask<Void>
    implements Runnable, AsynchronousCompletionTask {
    volatile Completion next;   // 下一个要执行的任务
}

4.2 原理分析

4.2.1 示例1

@Test
public void test9() throws ExecutionException, InterruptedException {
    CompletableFuture<String> base = new CompletableFuture<>();
    CompletableFuture<String> future = base.thenApply(s -> s + " 2").thenApply(s -> s + " 3");
    base.complete("1");
    System.out.println(future.get());
}
结果:1 2 3

在这里插入图片描述
链式调用,依赖于dep,组成一条单链表,按照从首到尾的顺序执行。

4.2.2 示例2

@Test
public void test10() {
    CompletableFuture<String> base = new CompletableFuture<>();
    CompletableFuture<String> future =
            base.thenApply(
                    s -> {
                        log.info("e");
                        return s + " 2";
                    });
    base.thenAccept(s -> log.info("c")).thenAccept(aVoid -> log.info("d"));
    base.thenAccept(s -> log.info("a")).thenAccept(aVoid -> log.info("b"));

    base.complete("1");
}
结果:
22:24:26.725 [main] INFO com.example.hellodello.thread.CompletableFutureTest - a
22:24:26.727 [main] INFO com.example.hellodello.thread.CompletableFutureTest - b
22:24:26.727 [main] INFO com.example.hellodello.thread.CompletableFutureTest - c
22:24:26.727 [main] INFO com.example.hellodello.thread.CompletableFutureTest - d
22:24:26.727 [main] INFO com.example.hellodello.thread.CompletableFutureTest - e

在这里插入图片描述

4.2.3 通用调用链路

整体采用:CAS链式栈 + 单链表的链路设计
在这里插入图片描述

五、人物志

编程不识Doug Lea, 写尽Java也枉然 – 来自微博

如果IT的历史,是以人为主体串接起来的话,那么肯定少不了Doug Lea。这个鼻梁挂着眼镜,留着德王威廉二世的胡子,脸上永远挂着谦逊腼腆笑容,服务于纽约州立大学Oswego分校计算机科学系的老大爷。他专门研究并发编程和并发数据结构的设计。

在这里插入图片描述

说他是这个世界上对Java影响力最大的个人,一点也不为过。因为两次Java历史上的大变革,他都间接或直接的扮演了举足轻重的角色。

  • 贡献1:一次是由JDK 1.1到JDK 1.2,JDK1.2很重要的一项新创举就是Collections,其Collections的概念可以说承袭自Doug Lea于1995年发布的第一个被广泛应用的collections;
  • 贡献2:一次是2004年所推出的Tiger。Tiger广纳了15项JSRs(Java Specification Requests)的语法及标准,其中一项便是JSR-166。JSR-166是来自于Doug编写的util.concurrent包

具体的示例:

-- 杰作HashMap-- java.util.concurrent 包;
-- AQS奠基者

社会评价:Doug是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为给了别人就减少了,知识的分享更能激荡出不一样的火花。《Effective JAVA》这本Java经典之作的作者Joshua Bloch便在书中特别感谢Doug Lea是此书中许多构想的共鸣板,感谢Doug Lea大方分享丰富而又宝贵的知识。

六、参考资料

极客时间-Java编程实战
http://ifeve.com/doug-lea/
https://gee.cs.oswego.edu/
https://developer.aliyun.com/article/712258
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

桃花猿

客官,赏点打酒钱

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

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

打赏作者

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

抵扣说明:

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

余额充值