hyperleoon / cffu

🦝 Java CompletableFuture Fu, aka. CF-Fu, pronounced "Shifu"; include best practice/traps guide and a tiny sidekick library for CompletableFuture.

Home Page:https://github.com/foldright/cffu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

🦝 CompletableFuture Fu (CF-Fu)

🚧 项目还在开发中没有发布。
工作项列表及其进展,参见 issue 6


Github Workflow Build Status Codecov Java support License GitHub Stars GitHub Forks GitHub issues GitHub Contributors gitpod: Ready to Code GitHub repo size

shifu

如何管理并发执行是个复杂易错的问题,业界有大量的工具、框架可以采用。

并发工具、框架的广度了解,可以看看如《七周七并发模型》、《Java虚拟机并发编程》、《Scala并发编程(第2版)》;更多关于并发主题的书籍参见书单

其中CompletableFuture (CF)有其优点:

  • Java标准库内置
    • 无需额外依赖,几乎总是可用
    • 相信有极高的实现质量
  • 广为人知广泛使用,有一流的群众基础
    • CompletableFuture在2014年发布的Java 8提供,有~10年了
    • CompletableFuture的父接口Future早在2004年发布的Java 5中提供,有~20年了
    • 虽然Future接口不支持 执行结果的异步获取与并发执行逻辑的编排,但也让广大Java开发者熟悉了Future这个典型的概念与工具
  • 功能强大、但不会非常庞大复杂
    • 足以应对日常的业务需求开发
    • 其它的大型并发框架(比如AkkaRxJava)在使用上需要理解的内容要多很多
    • 当然基本的并发关注方面及其复杂性,与具体使用哪个工具无关,都是要理解与注意的
  • 高层抽象
    • 或说 以业务流程的形式表达技术的并发流程
    • 可以不使用繁琐易错的基础并发协调工具,如CountDownLatch、锁(Lock)、信号量(Semaphore

和其它并发工具、框架一样,CompletableFuture 用于

  • 并发执行业务逻辑,或说编排并发的处理流程/处理任务
  • 利用多核并行处理
  • 提升业务响应性

值得更深入了解和应用。 💕



🎯 〇、目标

  • 作为文档库:
    • 完备说明CompletableFuture的使用方式
    • 给出 最佳实践建议 与 使用陷阱注意
    • 期望在业务中,更有效安全地使用CompletableFuture
    • 这部分是主要目标
  • 作为代码库:
    • 补齐在业务使用中CompletableFuture所缺失的功能
    • 期望在业务中,更方便自然地使用CompletableFuture
    • 这部分只是甜点目标

📚 一、CompletableFuture Guide

WIP...

为了阅读的简洁方便,后文CompletableFuture会简写成CF

CF并发执行的描述及其用语

cf-graph

WIP...

基本概念与术语:

  • 任务(Task)/计算(Computation)/执行(Execution
    • 任务逻辑(Task Logic)/业务逻辑(Biz Logic
  • 状态(State
    • 运行中(Running
    • 取消(Cancelled
    • 完成(Completed/Done
      • 成功(Successed/Success)/正常完成(Completed Normally)/成功完成(Completed Successfully
      • 失败(Failed/Fail)/异常完成(Completed Exceptionally
  • 业务流程(Biz Flow)、CF链(Chain
    • 流程图(Flow Graph)、有向无环图/DAG
      • 为什么构建的CF链一定是DAG
    • 流程编排(Flow Choreography
  • 前驱(Predecessor)/后继(Successor
    • 上游任务/前驱任务/Dependency Task(我依赖的任务)
    • 下游任务/后继任务/Dependent Task(依赖我的任务)
  • 状态变化、事件(Event)、触发(Trigger

CF并发执行的关注方面

CF任务执行/流程编排,即执行提交的代码逻辑/计算/任务,涉及下面4个方面:

  • 任务的输入输出
    • CF所关联任务的输入参数/返回结果(及其数据类型)
  • 任务的调度,即在哪个线程来执行任务。可以是
    • 在触发的线程中就地连续执行任务
    • 在指定Executor(的线程)中执行任务
  • 任务的错误处理(任务执行报错)
  • 任务的超时处理
    • 处理超时是并发的基础关注方面之一
    • 在实现上可以看成CF的使用方式
    • Java 9通过新增的completeOnTimeout(...)/orTimeout(...)方法提供了内置支持

本节『并发关注方面』会举例一些CF方法名,以说明CF方法的命名模式,可以先不用关心方法的具体功能。
在下一节『CF的功能』会分类展开说明CF方法的功能。

1. 输入输出

对应下面4种情况:

  • 无输入无返回(00)
    • 对应Runnable接口(包含单个run方法)
  • 无输入有返回(01)
    • 对应Supplier<O>接口(包含单个supply方法)
  • 有输入无返回(10)
    • 对应Consumer<I>接口(包含单个accept方法)
  • 有输入有返回(11)

注:

  • 对于有输入或返回的接口(即除Runnable接口)
    • 都是泛型的,所以可以支持不同的具体数据类型
    • 都是处理单个输入数据
    • 如果要处理两个输入数据,即有两个上游CF的返回,会涉及下面的变体接口
  • 对于有输入接口,有两个输入参数的变体接口:

CF通过其方法名中包含的用词来体现:

  • run:无输入无返回(00)
    • 即是Runnable接口包含的run方法名
    • 相应的CF方法名的一些例子:
      • runAsync(Runnable runnable)
      • thenRun(Runnable action)
      • runAfterBoth(CompletionStage<?> other, Runnable action)
      • runAfterEitherAsync(CompletionStage<?> other, Runnable action)
  • supply:无输入有返回(01)
    • 即是Supplier接口包含的supply方法名
    • 相应的CF方法名的一些例子:
      • supplyAsync(Supplier<U> supplier)
      • supplyAsync(Supplier<U> supplier, Executor executor)
  • accept:有输入无返回(10)
    • 即是Consumer接口包含的accept方法名
    • 相应的CF方法名的一些例子:
      • thenAccept(Consumer<T> action)
      • thenAcceptAsync(Consumer<T> action)
      • thenAcceptBoth(CompletionStage<U> other, BiConsumer<T, U> action)
      • acceptEitherAsync(CompletionStage<T> other, Consumer<T> action)
  • apply:有输入有返回(11)
    • 即是Function接口包含的apply方法名。CF的方法如
    • 相应的CF方法名的一些例子:
      • thenApply(Function<T, U> fn)
      • thenApplyAsync(Function<T, U> fn)
      • applyToEither(CompletionStage<T> other, Function<T, U> fn)

2. 调度

任务调度是指,任务在哪个线程执行。有2种方式:

  • 在触发的线程中就地连续执行任务
  • 在指定Executor(的线程)中执行任务

CF通过方法名后缀Async来体现调度方式:

  • 有方法名后缀Async
    • 在触发CF后,任务在指定Executor执行
      • 如果不指定executor参数,缺省是ForkJoinPool.commonPool()
    • 相应的CF方法名的一些例子:
      • runAsync(Runnable runnable)
      • thenAcceptAsync(Consumer<T> action, Executor executor)
      • runAfterBothAsync(CompletionStage<?> other, Runnable action)
  • 无方法名后缀Async
    • 任务在触发线程就地连续执行
    • 相应的CF方法名的一些例子:
      • thenAccept(Consumer<T> action)
      • thenApply(Function<T, U> fn)**
      • applyToEither(CompletionStage<T> other, Function<T, U> fn)

3. 错误处理

WIP...

4. 任务执行的超时处理

WIP...

CF的功能

1. CF的创建

通过静态工厂方法(🅵actory)或构造函数(🅒onstructor)来创建CompletableFuture。这些方法是CompletableFuture链的起始。

Method Name 🅒/🅵 结果类型 Executor
completedFuture(U value) 🅵 U 无需 用入参value直接创建一个已完成的CF,无需Executor来运行
completedStage(U value)J9 🅵 U 无需 与上一方法一样,只是返回的类型是CompletionStage而不CF
failedFuture(Throwable ex)J9 🅵 U 无需 用入参ex直接创建一个已完成的CF,无需Executor来运行
failedStage(Throwable ex)J9 🅵 U 无需 与上一方法一样,只是返回的类型是CompletionStage<U>而不CF
supplyAsync(Supplier<U> supplier) 🅵 U CF缺省Executor
supplyAsync(Supplier<U> supplier, Executor executor) 🅵 U executor入参
runAsync(Runnable runnable) 🅵 Void CF缺省Executor
runAsync(Runnable runnable, Executor executor) 🅵 Void executor入参
allOf(CompletableFuture<?>... cfs)〚1〛 🅵 Void 无需 组合输入的多个CF,本身无执行逻辑,所以无需Executor
anyOf(CompletableFuture<?>... cfs)〚1〛 🅵 Void 无需 同上
CompletableFuture<T>()〚2〛 🅒 T 无需 显式通过CF对象的写方法完成,无需Executor来运行

注:

  • 〚1〛:allOf/anyOf这个2个方法虽然是静态工厂方法;但不是CF链的起点,而是输入多个CF,用于编排多路的流程。
    • 在功能与使用的上,应该和下面【3. 流程编排】一节的方法归类在一起。
    • 这2个方法也列在上面的表格,只是为了体现出是静态工厂方法这个特点。
  • 〚2〛:在日常的业务开发中使用CF来编排业务流程,几乎一定不应该使用 这个构造方法。
    • 构造函数创建的CF的使用场景:在已有异步处理线程,即不与CF关联的Executor,显式调用CF对象的写方法设置其它结果;
    • 往往是在中间件中会有必要这样使用,比如在网络IO框架的回调(线程)中完成处理后设置CF结果。

2. CF的显式读写方法

读方法:

Method Name 所属父接口 阻塞?
boolean isDone() Future
T get() Future 阻塞❗
T get(long timeout, TimeUnit unit) Future 阻塞❗〚1〛
T getNow(T valueIfAbsent) -
T resultNow()J19 Future 返回已正常完成CF的正常结果;如果CF不是正常完成(未完成/被取消/异常完成)则抛出IllegalStateException异常
T join() - 阻塞❗️
boolean isCompletedExceptionally() -
Throwable exceptionNow()J19 Future 返回已异常完成CF的出错异常;如果CF不是异常完成(未完成/被取消/正常完成)则抛出IllegalStateException异常
boolean isCancelled() -
State state()J19 Future

注:

  • 〚1〛:T get(long timeout, TimeUnit unit)如果设置的超时是0,不会BLOCKING;但这个情况下应该调用T getNow(T valueIfAbsent)

写方法:

Method Name 所属父接口 阻塞?
boolean complete(T value) -
completeAsync(Supplier<T> supplier)J9 - 方法返回this,方便链式调用
completeAsync(Supplier<T> supplier, Executor executor)J9 - 同上
boolean completeExceptionally(Throwable ex) -
exceptionallyAsync(Function<Throwable, ? extends T> fn) -
boolean cancel(boolean mayInterruptIfRunning) Future
void obtrudeValue(T value) -
void obtrudeException(Throwable ex) -

3. CF的流程编排

WIP...

Method Name 所属父接口 阻塞?
completeOnTimeout(T value, long timeout, TimeUnit unit)J9 -
orTimeout(long timeout, TimeUnit unit)J9 -
delayedExecutor(long delay, TimeUnit unit, Executor executor)J9 -

4. 设计辅助方法

WIP...

CF的功能使用上,这些方法不是必须的。

但通过这些CF的非功能方法可以

  • 提升实现的安全性
    • 如防御式拷贝防止被使用方意外写结果
  • 获取额外信息
    • 如用于监控
  • ……
Method Name 结果类型
CompletableFuture<T> copy() T
CompletableFuture<U> newIncompleteFuture()J9〚1〛 T
CompletionStage<T> minimalCompletionStage()J9〚1〛 T
Executor defaultExecutor()J9 -
int getNumberOfDependents() -

注:

  • 〚1〛:CompletableFuture<U> newIncompleteFuture()功能与CompletableFuture<T>()是一样,实际上代码实现就只是调用构造函数。
    • 相比构造函数,工厂方法形式的一个好处是可以无需指定泛型参数;在很多库的API中都可以看到这样的设计方式。

CF的设计模式

WIP...

使用CF异步执行与主逻辑并发以缩短RT

CF的最佳实现与使用陷阱

WIP...

CF创建子CF(两个CF使用同一线程池),且阻塞等待子CF结果

会形成(池型)死锁。

WIP...

📦 二、库功能

WIP...

提供在业务使用中CompletableFuture所缺失的功能。

  • 运行多个CompletableFuture并返回结果的allOf方法:
    • resultAllOf方法,运行多个相同结果类型的CompletableFuture
      • CompletableFuture<List<T>> resultAllOf(CompletableFuture<T>... cfs)
      • CompletableFuture<List<T>> resultAllOf(List<? extends CompletableFuture<T>> cfs)
    • resultOf方法,运行多个不同结果类型的CompletableFuture
      • CompletableFuture<Pair<T1, T2>> resultOf(CompletableFuture<T1> cf1, CompletableFuture<T2> cf2)
      • CompletableFuture<Triple<T1, T2, T3>> resultOf(CompletableFuture<T1> cf1, CompletableFuture<T2> cf2, CompletableFuture<T3> cf3)
  • 类型安全的anyOf方法:
    • 提供的方法:
      • CompletableFuture<T> anyOf(CompletableFuture<T>... cfs)
      • CompletableFuture<T> anyOf(List<? extends CompletableFuture<T>> cfs)
    • CF返回的类型是Object,丢失具体类型:
      • CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

👋 ∞、关于库名

cffuCompletableFuture-Fu的缩写;读作C Fu,谐音Shifu/师傅

嗯嗯,想到了《功夫熊猫》里可爱的小浣熊师傅吧~ 🦝

shifu

About

🦝 Java CompletableFuture Fu, aka. CF-Fu, pronounced "Shifu"; include best practice/traps guide and a tiny sidekick library for CompletableFuture.

https://github.com/foldright/cffu

License:Apache License 2.0


Languages

Language:Java 83.2%Language:Kotlin 14.6%Language:Shell 2.2%