👉 cffu
(CompletableFuture-Fu
🦝)是一个小小的CompletableFuture(CF)
辅助增强库,提升CF
使用体验并减少误用,在业务中更方便高效安全地使用CF
。😋🚀🦺
欢迎 👏 💖
- 建议和提问,提交 Issue
- 贡献和改进,Fork 后提通过 Pull Request 贡献代码
- 🔧 功能
- 👥 User Guide
- 🔌 API Docs
- 🍪依赖
- 📚 更多资料
- 👋 关于库名
☘️ 补全业务使用中缺失的功能
- 🏪 更方便的功能,如
- 支持返回多个输入
CF
的运行结果,而不是不返回输入CF
结果(CompletableFuture#allOf
)
如方法allResultsFailFastOf
/allResultsOf
/mSupplyFailFastAsync
/thenMApplyFailFastAsync
- 支持返回多个不同类型的
CF
结果,而不是同一类型
如方法allTupleFailFastOf
/allTupleOf
/mSupplyTupleFailFastAsync
/thenMApplyTupleFailFastAsync
- 支持直接运行多个
Action
,而不是要先包装成CompletableFuture
如方法mSupplyTupleFailFastAsync
/mSupplyMostSuccessAsync
/thenMApplyTupleFailFastAsync
/thenMRunFailFastAsync
- 支持处理指定异常类型的
catching
方法,而不是处理所有异常Throwable
(CompletableFuture#exceptionally
)
- 支持返回多个输入
- 🚦 更高效灵活的并发执行策略,如
AllFailFast
策略:当输入的多个CF
有失败时快速失败返回,而不再于事无补地等待所有CF
运行完成(CompletableFuture#allOf
)AnySuccess
策略:返回首个成功的CF
结果,而不是首个完成但可能失败的CF
(CompletableFuture#anyOf
)AllSuccess
策略:返回多个CF
中成功的结果,对于失败的CF
返回指定的缺省值MostSuccess
策略:指定时间内返回多个CF
中成功的结果,对于失败或超时的CF
返回指定的缺省值All(Complete)
/Any(Complete)
策略:这2个是CompletableFuture
已有支持的策略
- 🦺 更安全的使用方式,如
- 支持设置缺省的业务线程池并封装携带,
CffuFactory#builder(executor)
方法 - 超时执行安全的
orTimeout
/completeOnTimeout
方法新实现
CF#orTimeout
/CF#completeOnTimeout
方法会导致CF
的超时与延迟执行基础功能失效❗️ - 一定不会修改
CF
结果的peek
处理方法
whenComplete
方法可能会修改CF
的结果,返回的CF
与输入CF
并不一定一致 - 支持超时的
join
的方法,join(timeout, unit)
方法 - 支持禁止强制篡改,
CffuFactoryBuilder#forbidObtrudeMethods
方法 - 在类方法附加完善的代码质量注解,在编码时
IDE
能尽早提示出问题
如@NonNull
、@Nullable
、@CheckReturnValue
、@Contract
等
- 支持设置缺省的业务线程池并封装携带,
- 🧩 缺失的基本功能,除了上面面向安全而新实现的方法,还有
- 异步异常完成,
completeExceptionallyAsync
方法 - 非阻塞地获取成功结果,如果
CF
失败或还在运行中则返回指定的缺省值,getSuccessNow
方法 - 解包装
CF
异常成业务异常,unwrapCfException
方法
- 异步异常完成,
⏳ Backport
支持Java 8
,Java 9+
高版本的所有CF
新功能方法在Java 8
低版本直接可用,如
- 超时控制:
orTimeout
/completeOnTimeout
- 延迟执行:
delayedExecutor
- 工厂方法:
failedFuture
/completedStage
/failedStage
- 处理操作:
completeAsync
/exceptionallyAsync
/exceptionallyCompose
/copy
- 非阻塞读:
resultNow
/exceptionNow
/state
💪 已有功能的增强,如
anyOf
方法:返回具体类型T
(类型安全),而不是返回Object
(CompletableFuture#anyOf
)allOf
/anyOf
方法:输入更宽泛的CompletionStage
参数类型,而不是CompletableFuture
类(CompletableFuture#allOf/anyOf
)
更多cffu
功能及其使用方式的说明详见 👥 User Guide。
如何管理并发执行是个复杂易错的问题,业界有大量的工具、框架可以采用。
并发工具、框架的广度了解,可以看看如《七周七并发模型》、《Java虚拟机并发编程》、《Scala并发编程(第2版)》;更多关于并发主题的书籍参见书单。
其中CompletableFuture(CF)
有其优点:
- 广为人知广泛使用,有一流的群众基础
CompletableFuture
在2014年发布的Java 8
提供,有10年了CompletableFuture
的父接口Future
早在2004年发布的Java 5
中提供,有20年了- 虽然
Future
接口不支持 运行结果的异步获取与并发执行逻辑的编排,但也让广大Java
开发者熟悉了Future
这个典型的概念与工具
Java
标准库内置- 无需额外依赖,几乎总是可用
- 相信有极高的实现质量
- 功能强大、但不会非常庞大复杂
- 高层抽象
- 或说 以业务流程的形式表达技术的并发流程
- 可以避免或减少使用繁琐易错的基础并发协调工具:同步器
Synchronizers
(如CountDownLatch
、CyclicBarrier
、Phaser
)、锁Locks
和原子类atomic
和其它并发工具、框架一样,CompletableFuture
用于
- 并发执行业务逻辑,或说编排并发处理流程或异步任务
- 多核并行处理,充分利用资源
- 提升业务响应性
值得更深入地了解和应用。 💕
- 🦝 使用
Cffu
类 - 🔧 使用
CompletableFutureUtils
工具类
推荐Cffu
类的使用方式: 🌟
- 相比
CompletableFutureUtils
的静态方法,新功能作为实例方法,可以自然方便地调用,就像使用CompletableFuture
一样 Java
语言不支持在已有类上扩展方法,所以需要一个新的包装类
如果你不想在项目中引入新类(Cffu
类)、觉得这样增加了复杂性的话,完全可以把cffu
库作为一个工具类来用:
- 优化
CompletableFuture
使用的工具方法在业务项目中很常见,CompletableFutureUtils
提供了一系列实用可靠高效安全的工具方法 - 这种使用方式有些
cffu
功能没有提供(也没有想到好的实现方案)
如支持设置缺省的业务线程池、禁止强制篡改
直接使用CompletableFuture
类的代码可以比较简单的迁移到Cffu
类,包含2步修改:
- 在类型声明地方,由
CompletableFuture
改成Cffu
- 在
CompletableFuture
静态方法调用的地方,类名CompletableFuture
改成cffuFactory
实例 - 更多参见如何从直接使用
CompletableFuture
类迁移到Cffu
类
库依赖(包含Java CompletableFuture
的增强CompletableFutureUtils
):
-
For
Maven
projects:<dependency> <groupId>io.foldright</groupId> <artifactId>cffu</artifactId> <version>1.0.0-Alpha31</version> </dependency>
-
For
Gradle
projects:Gradle Kotlin DSL
implementation("io.foldright:cffu:1.0.0-Alpha31")
Gradle Groovy DSL
implementation 'io.foldright:cffu:1.0.0-Alpha31'
cffu
也支持Kotlin
扩展方法的使用方式,参见cffu-kotlin/README.md
;使用方式的对比示例参见docs/usage-mode-demo.md
。
CompletableFuture
的allOf
方法不返回多个CF
的运行结果(方法返回类型是CF<Void>
)。
由于不能方便地获取多个CF
的运行结果,需要:
- 在
allOf
方法之后再通过入参CF
的读操作(如join
/get
)来获取结果- 操作繁琐
- 像
join
/get
读方法是阻塞的,增加了业务逻辑的死锁风险❗️
更多说明可以看看CompletableFuture原理与实践 - 4.2.2 线程池循环引用会导致死锁
- 或是在传入的
CompletableFuture Action
中设置外部的变量,需要注意多线程读写的线程安全问题⚠️ - 多线程读写涉及多线程数据传递的复杂性,遗漏并发逻辑的数据读写的正确处理是业务代码中的常见问题❗️
- 并发深坑勿入,并发逻辑复杂易出Bug 🐞
如果涉及超时则会更复杂,JDK CompletableFuture
自身在Java 21
中也有这方面的Bug修复 ⏰
cffu
的allResultsFailFastOf
/ allResultsOf
/ mostSuccessResultsOf
等方法提供了返回多个CF
运行结果的功能。使用这些方法获取多个CF
的整体运行结果:
- 方便直接
- 因为返回的是有整体结果的
CF
,可以继续串接非阻塞的操作,所以自然减少了阻塞读方法(如join
/get
)的使用,尽量降低业务逻辑的死锁风险 - 规避了在业务逻辑中直接实现多线程读写逻辑的复杂线程安全问题与逻辑错误
- 使用「可靠实现与测试的」库的并发功能而不是去直接实现 是 最佳实践 🌟
示例代码如下:
public class AllResultsOfDemo {
public static final Executor myBizExecutor = Executors.newCachedThreadPool();
public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();
public static void main(String[] args) throws Exception {
//////////////////////////////////////////////////
// CffuFactory#allResultsOf
//////////////////////////////////////////////////
Cffu<Integer> cffu1 = cffuFactory.completedFuture(21);
Cffu<Integer> cffu2 = cffuFactory.completedFuture(42);
Cffu<Void> all = cffuFactory.allOf(cffu1, cffu2);
// Result type is Void!
//
// the result can be got by input argument `cf1.get()`, but it's cumbersome.
// so we can see a lot of util methods to enhance `allOf` with result in our project.
Cffu<List<Integer>> allResults = cffuFactory.allResultsOf(cffu1, cffu2);
System.out.println(allResults.get());
//////////////////////////////////////////////////
// or CompletableFutureUtils#allResultsOf
//////////////////////////////////////////////////
CompletableFuture<Integer> cf1 = CompletableFuture.completedFuture(21);
CompletableFuture<Integer> cf2 = CompletableFuture.completedFuture(42);
CompletableFuture<Void> all2 = CompletableFuture.allOf(cf1, cf2);
// Result type is Void!
CompletableFuture<List<Integer>> allResults2 = allResultsOf(cf1, cf2);
System.out.println(allResults2.get());
}
}
# 完整可运行的Demo代码参见
AllResultsOfDemo.java
。
上面是多个相同结果类型的CF
,cffu
还提供了返回多个不同类型CF
结果的allTupleFailFastOf
/ allTupleOf
/ mSupplyTupleFailFastAsync
等方法。
示例代码如下:
public class AllTupleOfDemo {
public static final Executor myBizExecutor = Executors.newCachedThreadPool();
public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();
public static void main(String[] args) throws Exception {
//////////////////////////////////////////////////
// allTupleFailFastOf / allTupleOf
//////////////////////////////////////////////////
Cffu<String> cffu1 = cffuFactory.completedFuture("21");
Cffu<Integer> cffu2 = cffuFactory.completedFuture(42);
Cffu<Tuple2<String, Integer>> allTuple = cffuFactory.allTupleFailFastOf(cffu1, cffu2);
System.out.println(allTuple.get());
//////////////////////////////////////////////////
// or CompletableFutureUtils.allTupleFailFastOf / allTupleOf
//////////////////////////////////////////////////
CompletableFuture<String> cf1 = CompletableFuture.completedFuture("21");
CompletableFuture<Integer> cf2 = CompletableFuture.completedFuture(42);
CompletableFuture<Tuple2<String, Integer>> allTuple2 = allTupleFailFastOf(cf1, cf2);
System.out.println(allTuple2.get());
}
}
# 完整可运行的Demo代码参见
AllTupleOfDemo.java
。
CompletableFuture
异步执行(即CompletableFuture
的*Async
方法),使用的缺省线程池是ForkJoinPool.commonPool()
。- 这个线程池差不多是
CPU
个线程,合适执行CPU
密集的任务;对于业务逻辑,往往有很多等待操作(如网络IO
、阻塞等待),并不是CPU
密集的。 - 业务使用这个缺省线程池
ForkJoinPool.commonPool()
是很危险的❗
结果就是,
- 在业务逻辑中,调用
CompletableFuture
的*Async
方法时,几乎每次都要反复传入指定的业务线程池;这让CompletableFuture
的使用很繁琐易错 🤯 - 在底层逻辑中,当底层操作回调业务时(如
RPC
回调),不合适或方便为业务提供线程池;这时使用Cffu
封装携带的线程池既方便又合理安全
这个使用场景更多可以看看CompletableFuture原理与实践 - 4.2.3 异步RPC调用注意不要阻塞IO线程池
示例代码如下:
public class NoDefaultExecutorSettingForCompletableFuture {
public static final Executor myBizExecutor = Executors.newCachedThreadPool();
public static void main(String[] args) {
CompletableFuture<Void> cf1 = CompletableFuture.runAsync(
() -> System.out.println("doing a long time work!"),
myBizExecutor);
CompletableFuture<Void> cf2 = CompletableFuture
.supplyAsync(
() -> {
System.out.println("doing another long time work!");
return 42;
},
myBizExecutor)
.thenAcceptAsync(
i -> System.out.println("doing third long time work!"),
myBizExecutor);
CompletableFuture.allOf(cf1, cf2).join();
}
}
# 完整可运行的Demo代码参见
NoDefaultExecutorSettingForCompletableFuture.java
。
Cffu
支持设置缺省的业务线程池,规避上面的繁琐与危险。示例代码如下:
public class DefaultExecutorSettingForCffu {
public static final Executor myBizExecutor = Executors.newCachedThreadPool();
public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();
public static void main(String[] args) {
Cffu<Void> cf1 = cffuFactory.runAsync(() -> System.out.println("doing a long time work!"));
Cffu<Void> cf2 = cffuFactory.supplyAsync(() -> {
System.out.println("doing another long time work!");
return 42;
}).thenAcceptAsync(i -> System.out.println("doing third long time work!"));
cffuFactory.allOf(cf1, cf2).join();
}
}
# 完整可运行的Demo代码参见
DefaultExecutorSettingForCffu.java
。
CompletableFuture
的allOf
方法会等待所有输入CF
运行完成;即使有CF
失败了也要等待后续CF
都运行完成,再返回一个失败的CF
。- 对于业务逻辑来说,这样失败且继续等待策略,减慢了业务响应性;会希望如果有输入
CF
失败了,则快速失败不再做于事无补的等待 cffu
提供了相应的allResultsFailFastOf
等方法allOf
/allResultsFailFastOf
两者都是,只有当所有的输入CF
都成功时,才返回成功结果
- 对于业务逻辑来说,这样失败且继续等待策略,减慢了业务响应性;会希望如果有输入
CompletableFuture
的anyOf
方法返回首个完成的CF
(不会等待后续没有完成的CF
,赛马模式);即使首个完成的CF
是失败的,也会返回这个失败的CF
结果。- 对于业务逻辑来说,会希望赛马模式返回首个成功的
CF
结果,而不是首个完成但失败的CF
cffu
提供了相应的anySuccessOf
等方法anySuccessOf
只有当所有的输入CF
都失败时,才返回失败结果
- 对于业务逻辑来说,会希望赛马模式返回首个成功的
- 返回多个
CF
中成功的结果,对于失败的CF
返回指定的缺省值- 业务有容错逻辑时,当某些
CF
处理出错时可以使用成功的那部分结果,而不是整体处理失败 cffu
提供了相应的allSuccessOf
等方法
- 业务有容错逻辑时,当某些
- 返回指定时间内多个
CF
中成功的结果,对于失败或超时的CF
返回指定的缺省值- 业务最终一致性时,能返回就尽量返回有的;对于没有及时返回还在运行中处理的
CF
,结果会写到分布式缓存中避免重复计算,下次业务请求就有了 - 这是个常见业务使用模式,
cffu
提供了相应的mostSuccessResultsOf
等方法
- 业务最终一致性时,能返回就尽量返回有的;对于没有及时返回还在运行中处理的
📔 关于多个
CF
的并发执行策略,可以看看JavaScript
规范Promise Concurrency
;在JavaScript
中,Promise
即对应CompletableFuture
。
JavaScript Promise
提供了4个并发执行方法:
Promise.all()
:等待所有Promise
运行成功,只要有一个失败就立即返回失败(对应cffu
的allResultsFailFastOf
方法)Promise.allSettled()
:等待所有Promise
运行完成,不管成功失败(对应cffu
的allResultsOf
方法)Promise.any()
:赛马模式,立即返回首个成功的Promise
(对应cffu
的anySuccessOf
方法)Promise.race()
:赛马模式,立即返回首个完成的Promise
(对应cffu
的anyOf
方法)PS:
JavaScript Promise
的方法命名真考究~ 👍
cffu
新加2个方法后,对齐了JavaScript Promise
规范的并发方法~ 👏
示例代码如下:
public class ConcurrencyStrategyDemo {
public static final Executor myBizExecutor = Executors.newCachedThreadPool();
public static final CffuFactory cffuFactory = CffuFactory.builder(myBizExecutor).build();
public static void main(String[] args) throws Exception {
////////////////////////////////////////////////////////////////////////
// CffuFactory#allResultsFailFastOf
// CffuFactory#anySuccessOf
// CffuFactory#mostSuccessResultsOf
////////////////////////////////////////////////////////////////////////
final Cffu<Integer> successAfterLongTime = cffuFactory.supplyAsync(() -> {
sleep(3000); // sleep LONG time
return 42;
});
final Cffu<Integer> failed = cffuFactory.failedFuture(new RuntimeException("Bang!"));
Cffu<List<Integer>> failFast = cffuFactory.allResultsFailFastOf(successAfterLongTime, failed);
// fail fast without waiting successAfterLongTime
System.out.println(failFast.exceptionNow());
Cffu<Integer> anySuccess = cffuFactory.anySuccessOf(successAfterLongTime, failed);
System.out.println(anySuccess.get());
Cffu<List<Integer>> mostSuccess = cffuFactory.mostSuccessResultsOf(
0, 100, TimeUnit.MILLISECONDS, successAfterLongTime, failed);
System.out.println(mostSuccess.get());
////////////////////////////////////////////////////////////////////////
// or CompletableFutureUtils#allResultsFailFastOf
// CompletableFutureUtils#anySuccessOf
// CompletableFutureUtils#mostSuccessResultsOf
////////////////////////////////////////////////////////////////////////
final CompletableFuture<Integer> successAfterLongTimeCf = CompletableFuture.supplyAsync(() -> {
sleep(3000); // sleep LONG time
return 42;
});
final CompletableFuture<Integer> failedCf = failedFuture(new RuntimeException("Bang!"));
CompletableFuture<List<Integer>> failFast2 = allResultsFailFastOf(successAfterLongTimeCf, failedCf);
// fail fast without waiting successAfterLongTime
System.out.println(exceptionNow(failFast2));
CompletableFuture<Integer> anySuccess2 = anySuccessOf(successAfterLongTimeCf, failedCf);
System.out.println(anySuccess2.get());
CompletableFuture<List<Integer>> mostSuccess2 = mostSuccessResultsOf(
0, 100, TimeUnit.MILLISECONDS, successAfterLongTime, failed);
System.out.println(mostSuccess2.get());
}
}
# 完整可运行的Demo代码参见
ConcurrencyStrategyDemo.java
。
CompletableFuture
的allOf/anyOf
方法输入的是CompletableFuture
,当业务直接有要编排业务逻辑方法时仍然需要先包装成CompletableFuture
再运行:
- 繁琐
- 也模糊了业务流程
cffu
提供了直接运行多个Action
的方法,方便直接明了地表达业务编排流程。
示例代码如下:
public class MultipleActionsDemo {
static void mRunAsyncDemo() {
// wrap tasks to CompletableFuture first, AWKWARD! 😖
CompletableFuture.allOf(
CompletableFuture.runAsync(() -> System.out.println("task1")),
CompletableFuture.runAsync(() -> System.out.println("task2")),
CompletableFuture.runAsync(() -> System.out.println("task3"))
);
// just run multiple actions, fresh and cool 😋
CompletableFutureUtils.mRunAsync(
() -> System.out.println("task1"),
() -> System.out.println("task2"),
() -> System.out.println("task3")
);
}
}
这些多Action
方法也配套实现了「不同的并发执行策略」与「返回多个运行结果」的支持。
示例代码如下:
public class MultipleActionsDemo {
static void thenMApplyAsyncDemo() {
// wrap tasks to CompletableFuture first, AWKWARD! 😖
completedFuture(42).thenCompose(v ->
CompletableFutureUtils.allResultsFailFastOf(
CompletableFuture.supplyAsync(() -> v + 1),
CompletableFuture.supplyAsync(() -> v + 2),
CompletableFuture.supplyAsync(() -> v + 3)
)
).thenAccept(System.out::println);
// output: [43, 44, 45]
// just run multiple actions, fresh and cool 😋
CompletableFutureUtils.thenMApplyFailFastAsync(
completedFuture(42),
v -> v + 1,
v -> v + 2,
v -> v + 3
).thenAccept(System.out::println);
// output: [43, 44, 45]
}
}
# 完整可运行的Demo代码参见
MultipleActionsDemo.java
。
Java 9+
高版本的所有CF
新功能方法在Java 8
低版本直接可用。
其中重要的backport
功能有:
- 超时控制:
orTimeout
/completeOnTimeout
- 延迟执行:
delayedExecutor
- 工厂方法:
failedFuture
/completedStage
/failedStage
- 处理操作:
completeAsync
/exceptionallyAsync
/exceptionallyCompose
/copy
- 非阻塞读:
resultNow
/exceptionNow
/state
这些backport
方法是CompletableFuture
的已有功能,不附代码示例。
CF#orTimeout
/ CF#completeOnTimeout
方法在超时使用内部的单线程ScheduledThreadPoolExecutor
来触发业务逻辑执行,会导致CF
的超时与延迟执行基础功能失效❗️
因为超时与延迟执行是基础功能,一旦失效会导致:
- 业务功能的正确性问题
- 系统稳定性问题,如导致线程中等待操作不能返回、耗尽线程池
cffu
提供了超时执行安全的新实现方法 cffuOrTimeout()
/ cffuCompleteOnTimeout()
。
更多说明参见:
- 演示问题的
DelayDysfunctionDemo.java
cffu backport
方法的JavaDoc
cf.join()
会「不超时永远等待」,在业务中很危险❗️当意外出现长时间等待时,会导致:
- 主业务逻辑阻塞,没有机会做相应的处理,以及时响应用户
- 会费掉一个线程,线程是很有限的资源(一般几百个),耗尽线程意味着服务瘫痪故障
join(timeout, unit)
方法即支持超时的join
的方法;就像cf.get(timeout, unit)
之于 cf.get()
。
这个新方法使用简单类似,不附代码示例。
CompletableFuture.anyOf
方法返回类型是Object
,丢失具体类型,不类型安全,使用时需要转型也不方便。
cffu
提供的anySuccessOf()
/ anyOf()
方法,返回具体类型T
,而不是返回Object
。
这个方法使用简单类似,不附代码示例。
CompletableFuture#allOf/anyOf
方法输入参数类型是CompletableFuture
,而输入更宽泛的CompletionStage
类型;对于CompletionStage
类型的输入,则需要调用CompletionStage#toCompletableFuture
方法做转换。
cffu
提供的allOf()
/ anyOf()
方法输入更宽泛的CompletionStage
参数类型,使用更方便。
方法使用简单类似,不附代码示例。
可以参见:
为了方便地使用cffu
的增强功能与方法,可以迁移直接使用CompletableFuture
类的已有代码到Cffu
类:
- 在类型声明地方,
CompletableFuture
类改成Cffu
类 - 在
CompletableFuture
静态方法调用的地方,类名CompletableFuture
改成cffuFactory
实例
之所以可以这样迁移,是因为:
CompletableFuture
类的所有实例方法都在Cffu
类,且有相同的方法签名与功能CompletableFuture
类的所有静态方法都在CffuFactory
类,且有相同的方法签名与功能
- 当前版本的
Java API
文档: https://foldright.io/api-docs/cffu/
代码示例:
可以在 central.sonatype.com 查看最新版本与可用版本列表。
cffu
库(包含Java CompletableFuture
的增强CompletableFutureUtils
):-
For
Maven
projects:<dependency> <groupId>io.foldright</groupId> <artifactId>cffu</artifactId> <version>1.0.0-Alpha31</version> </dependency>
-
For
Gradle
projects:Gradle Kotlin DSL
implementation("io.foldright:cffu:1.0.0-Alpha31")
Gradle Groovy DSL
implementation 'io.foldright:cffu:1.0.0-Alpha31'
-
cffu bom
:-
For
Maven
projects:<dependency> <groupId>io.foldright</groupId> <artifactId>cffu-bom</artifactId> <version>1.0.0-Alpha31</version> <type>pom</type> <scope>import</scope> </dependency>
-
For
Gradle
projects:Gradle Kotlin DSL
implementation(platform("io.foldright:cffu-bom:1.0.0-Alpha31"))
Gradle Groovy DSL
implementation platform('io.foldright:cffu-bom:1.0.0-Alpha31')
-
- 📌
TransmittableThreadLocal(TTL)
的cffu executor wrapper SPI
实现:-
For
Maven
projects:<dependency> <groupId>io.foldright</groupId> <artifactId>cffu-ttl-executor-wrapper</artifactId> <version>1.0.0-Alpha31</version> <scope>runtime</scope> </dependency>
-
For
Gradle
projects:Gradle Kotlin DSL
runtimeOnly("io.foldright:cffu-ttl-executor-wrapper:1.0.0-Alpha31")
Gradle Groovy DSL
runtimeOnly 'io.foldright:cffu-ttl-executor-wrapper:1.0.0-Alpha31'
-
- 官方资料
cffu
开发者@linzee1
的CF/cffu
掘金专栏CompletableFuture
Guide- 完备说明
CompletableFuture
的使用方式 - 给出 最佳实践建议 与 使用陷阱注意
- 在业务中,更有效安全地使用
CompletableFuture
- 完备说明
cffu
是 CompletableFuture-Fu
的缩写;读作C Fu
,谐音Shifu/师傅
。
嗯嗯,想到了《功夫熊猫》里可爱的小浣熊师傅吧~ 🦝