如何安全发布 CompletableFuture ?Java9新增方法分析

本文未经允许禁止转载。

JDK9 中对于CompletableFuture做了新的增强,除了超时功能(orTimeout),还有面向继承、安全发布等相关方法。本文中,我们将详细分析各个新增方法,同时说明其安全发布的重要性,最后提出相关的实践原则。

1. newIncompleteFuture

public <U> CompletableFuture<U> newIncompleteFuture() {
return new CompletableFuture<U>();
}

这是一个面向继承的方法,Java支持方法返回值协变,子类可以返回自己的实现。如果你不需要继承 CompletableFuture,对于使用来说,这个方法没有新增新的功能。

2. 默认执行器

public Executor defaultExecutor() {
return ASYNC_POOL;
}

子类可以指定默认执行器,不过需要注意调用不显式指定执行器的async相关方法时,其执行回调所在的线程有两种可能:当前调用线程或者子类指定的执行器线程。如果使用 CompletableFuture,推荐显式指定执行器;如果使用 CFFU,无需每次都显式指定执行器。

3. 复制方法

public CompletableFuture<T> copy() {
return uniCopyStage(this);
}

此方法等同于调用 cf.thenApply(x -> x), 目的是实现保护性复制,避免后续操作对于原实例的修改。

4. 只读形式与安全发布

// 返回结果仅支持 CompletionStage 操作
public CompletionStage<T> minimalCompletionStage() {
return uniAsMinimalStage();
} private MinimalStage<T> uniAsMinimalStage() {
Object r;
// 性能优化1:若已有结果,当前线程下直接返回stage
if ((r = result) != null)
return new MinimalStage<T>(encodeRelay(r));
// 性能优化2:若还未有结果,注册回调
MinimalStage<T> d = new MinimalStage<T>();
unipush(new UniRelay<T,T>(d, this));
return d;
} // 实际实现,CompletableFuture 独有方法均不支持
static final class MinimalStage<T> extends CompletableFuture<T> {
MinimalStage() { }
MinimalStage(Object r) { super(r); }
@Override public <U> CompletableFuture<U> newIncompleteFuture() {
return new MinimalStage<U>(); }
@Override public T get() {
throw new UnsupportedOperationException(); }
@Override public T get(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); }
@Override public T getNow(T valueIfAbsent) {
throw new UnsupportedOperationException(); }
@Override public T join() {
throw new UnsupportedOperationException(); }
@Override public T resultNow() {
throw new UnsupportedOperationException(); }
@Override public Throwable exceptionNow() {
throw new UnsupportedOperationException(); }
@Override public boolean complete(T value) {
throw new UnsupportedOperationException(); }
@Override public boolean completeExceptionally(Throwable ex) {
throw new UnsupportedOperationException(); }
@Override public boolean cancel(boolean mayInterruptIfRunning) {
throw new UnsupportedOperationException(); }
@Override public void obtrudeValue(T value) {
throw new UnsupportedOperationException(); }
@Override public void obtrudeException(Throwable ex) {
throw new UnsupportedOperationException(); }
@Override public boolean isDone() {
throw new UnsupportedOperationException(); }
@Override public boolean isCancelled() {
throw new UnsupportedOperationException(); }
@Override public boolean isCompletedExceptionally() {
throw new UnsupportedOperationException(); }
@Override public State state() {
throw new UnsupportedOperationException(); }
@Override public int getNumberOfDependents() {
throw new UnsupportedOperationException(); }
@Override public CompletableFuture<T> completeAsync
(Supplier<? extends T> supplier, Executor executor) {
throw new UnsupportedOperationException(); }
@Override public CompletableFuture<T> completeAsync
(Supplier<? extends T> supplier) {
throw new UnsupportedOperationException(); }
@Override public CompletableFuture<T> orTimeout
(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); }
@Override public CompletableFuture<T> completeOnTimeout
(T value, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); }
// 返回新的CompletableFuture, 等效于thenApply(x -> x)
@Override public CompletableFuture<T> toCompletableFuture() {
Object r;
if ((r = result) != null)
return new CompletableFuture<T>(encodeRelay(r));
else {
CompletableFuture<T> d = new CompletableFuture<>();
unipush(new UniRelay<T,T>(d, this));
return d;
}
}
}

此方法返回只读的 CompletionStage,可以实现安全发布。可以类比于一个方法返回了ImmutableList,优点是可以防止后续的错误操作,比如其他线程强制写结果(类比List#add)。

有这样一个安全发布的保证至关重要,特别是面临复杂场景时,可以减轻编程负担,放心地使用 CompletableFuture 返回的结果。

我们知道CompletionStage接口定义了链式异步回调相关方法, CompletableFuture 相关读写操作(join, complete, obtrudeValue 等),不在CompletionStage的定义下,可以通过方法toCompletableFuture进行中转。

class InventoryServiceDemo {
final Executor executor = Executors.newCachedThreadPool(); // 获取库存信息
// 方法可以返回 CompletionStage,相比于CompletableFuture更安全
CompletionStage<Integer> getInventoryAsync(String productId) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(Duration.ofSeconds(2));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 42;
}, executor)
.minimalCompletionStage();
}
}

笔者曾在《深入理解 Future, CompletableFuture, ListenableFuture,回调机制》一文中质疑 CompletionStage#toCompletableFuture 方法破坏了接口设计的相关原则,CompletableFuture#minimalCompletionStage 不如直接使用接口。

  1. CompletionStage#toCompletableFuture 表明 CompletableFuture 可以作为 CompletionStage 的默认实现,实现两者类型的快速转换。很多情况下,两者的区别不大。
  2. CompletableFuture#minimalCompletionStage 底层思想是提供只读功能。
  3. 这里的实现有点取巧,minimalStage虽然继承自CompleatableFuture,而实际上只是实现了CompletionStage 接口方法。minimalStage 不应该是 CompletableFuture的示例,更好的实现方式是使用委托方式(组合)。
  4. 如果子类进行类型检查,使用 instanceOf 或者新版JDK支持的模式匹配,minimalStage 需要进行额外的处理。
  5. CFFU 的实现对于minimalStage做了兼容处理,你可以放心使用。

5. 异步写操作

public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier) {
return completeAsync(supplier, defaultExecutor());
} public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier,
Executor executor) {
if (supplier == null || executor == null)
throw new NullPointerException();
executor.execute(new AsyncSupply<T>(this, supplier));
return this;
}

这两个方法很容易理解,适用于子类返回自己的实现。CompletableFuture#supplyAsync 工厂方法返回的实例是 ComplteableFuture 无法更改。子类可以自己定义自己的工厂方法;用户也可以使用这里的异步写方法。

6. CFFU的优化拓展

开源项目CFFU(功夫未来)对于CompletableFuture 进行了拓展,以下仅列举和Java9新增功能相关的拓展点:

  1. 可以在对象创建时指定执行器,后续操作无需重复指定
  2. Java8 版本下可以使用后续版本的功能
  3. 提供了相关安全发布功能,比如安全的超时功能
  4. 内部的很多便利方法应用了保护性复制方法
  5. CffuFactoryBuilder 支持以参数形式配置是否允许强制重写结果

7. 实践原则

  1. 异步方法可以返回 CompletionStage,其相比于CompletableFuture更为安全,同时可以提示用户不要使用阻塞相关方法
  2. 就像推荐使用不可变对象一样,推荐默认使用 CompletableFuture#minimalCompletionStage 方法
  3. 无论是对于方法入参还是返回结果,使用接口一般都优于实体类
  4. 理想的 Future 设计应该是读写分离的,不可变的。虽然 CompletableFuture 并没有完全遵循以上原则,但是我们在使用时应当注意遵循。

如何安全发布 CompletableFuture ?Java9新增方法分析的更多相关文章

  1. SpringData-Redis发布订阅自动重连分析

    SpringData-Redis发布订阅自动重连分析 RedisMessageListenerContainer 配置 @Bean @Autowired RedisMessageListenerCon ...

  2. SpringBoot框架——从SpringBoot看IoC容器初始化流程之方法分析

    目录 一.概观Spring Boot 二.Spring Boot应用初始化 2.1 初始化入口 2.2 SpringApplication的run方法 2.3 方法分析 三.容器创建与初始化 3.1 ...

  3. 【Java并发编程实战】-----“J.U.C”:ReentrantLock之三unlock方法分析

    前篇博客LZ已经分析了ReentrantLock的lock()实现过程,我们了解到lock实现机制有公平锁和非公平锁,两者的主要区别在于公平锁要按照CLH队列等待获取锁,而非公平锁无视CLH队列直接获 ...

  4. HTML5的新增方法

    json的新增方法: parse()   将JSON转换为字符串:必须是严格的JSON格式: 用法 : var s = {"name":"name"}; JSO ...

  5. php模拟登陆的两种实现方法分析

    php模拟登陆的实现方法分析 本文实例分析了php模拟登陆的实现方法.分享给大家供大家参考.具体分析如下: php模拟登陆的实现方法,这里分别列举两种方法实现模拟登陆人人网.具体实例代码如下: 1)使 ...

  6. Servlet生命周期中的service方法分析

    问题ServletLifeCycle中的service方法内,有super.service(request, response); 会执行this.doGet(HttpServletRequest r ...

  7. KCF目标跟踪方法分析与总结

    KCF目标跟踪方法分析与总结 correlation filter Kernelized correlation filter tracking 读"J. F. Henriques, R. ...

  8. SQL2005删除复制数据库的发布与订阅的方法(转载)

    SQL2005删除复制数据库的发布与订阅的方法 --在测试环境中恢复从正式数据库服务器 上备份下来的bak文件后,正式环境里数据库复制的发布.订阅也被带进来了,结果恢复的数据库无法更改表结构,直接删除 ...

  9. 【转】第7篇:Xilium CefGlue 关于 CLR Object 与 JS 交互类库封装报告:全自动注册与反射方法分析

    作者: 牛A与牛C之间 时间: 2013-12-12 分类: 技术文章 | 2条评论 | 编辑文章 主页 » 技术文章 » 第7篇:Xilium CefGlue 关于 CLR Object 与 JS ...

  10. 【转】第6篇:Xilium CefGlue 关于 CLR Object 与 JS 交互类库封装报告:自动注册JS脚本+自动反射方法分析

    作者: 牛A与牛C之间 时间: 2013-11-21 分类: 技术文章 | 暂无评论 | 编辑文章 主页 » 技术文章 » 第6篇:Xilium CefGlue 关于 CLR Object 与 JS ...

随机推荐

  1. Python 潮流周刊#78:async/await 是糟糕的设计(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  2. 外网如何通过https访问自己的服务

    前情提要 最近在接wx小游戏的sdk, 消息推送需要在wx端配置https开头的地址. 我本地开了一个http服务,我的ip为192.168.40.39, 监听端口 8091.想要达到的效果是,外部浏 ...

  3. CF926 Div.2

    C. Sasha and the Casino 赌场规则:如果下注 \(y(y > 0)\) 元,如果赢了则除了 \(y\) 元外,额外获得 \(y \times (k - 1)\) 元,否则则 ...

  4. 将ipynb文件转成pdf

    本文内容:将GitHub上ipynb源码格式的书籍转成pdf 应用场景:GitHub上某些书籍按章节使用ipynb格式存储 (Jupyter创建了一种良好的交互方式,即将程序说明和代码放在同一个文档中 ...

  5. 试了下Cursor,感觉程序员工种危险了

    大家好,我是汤师爷~ 今年8月份,AI 编程工具 Cursor 在开发者社区彻底火了.在 Twitter 平台上,Cloudflare 副总裁分享了一段视频,展示了一个令人震惊的案例.他年仅 8 岁的 ...

  6. 内存Fuzz和WinAFL

    文章一开始发表在微信公众号 https://mp.weixin.qq.com/s/XSPrmBb44J8BUpKsj0cwGQ 内存Fuzz和WinAFL FoxitReader 软件分析 目前Fuz ...

  7. 鸿蒙NEXT开发案例:颜文字搜索器

    [引言] 本文将介绍一个名为"颜文字搜索器"的开发案例,该应用是基于鸿蒙NEXT平台构建的,旨在帮助用户快速查找和使用各种风格的表情符号.通过本案例的学习,读者可以了解如何在鸿蒙平 ...

  8. HTML 面试题

    .code { background-color: rgba(246, 246, 246, 1); color: rgba(232, 62, 140, 1) } DOCTYPE的作用? DOCTYPE ...

  9. 明察秋毫--用ss工具统计网络栈内存使用

    前言 本文介绍了用ss工具来统计一下当前网络栈的内存使用情况 环境准备 组件 版本 操作系统 Ubuntu 22.04.4 LTS 查看socket内存相关参数,-m 参数 ss -tm State ...

  10. Qt音视频开发41-文件推流(支持网页和播放器播放并切换进度)

    一.前言 本功能最初也是有一些人提过类似的需求,就是能不能将本地的音视频文件,通过纯Qt程序推流出去,然后用户可以直接在网页上播放,也可以用各种播放器播放,然后还可以任意切换播放进度,其实说白了就是个 ...