并发编程(三)Promise, Future 和 Callback
并发编程(三)Promise, Future 和 Callback
异步操作的有两个经典接口:Future 和 Promise,其中的 Future 表示一个可能还没有实际完成的异步任务的结果,针对这个结果可以添加 Callback 以便在任务执行成功或失败后做出对应的操作,而 Promise 交由任务执行者,任务执行者通过 Promise 可以标记任务完成或者失败。 可以说这一套模型是很多异步非阻塞架构的基础。
这一套经典的模型在 Scala、C# 中得到了原生的支持,但 JDK 中暂时还只有无 Callback 的 Future 出现,当然也并非在 Java 界就没有发展了,比如 Guava 就提供了 ListenableFuture 接口,而 Netty 4+ 更是提供了完整的 Promise、Future 和 Listener 机制。
一、Future 模式 - 将来式(JDK)
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(() -> {
TimeUnit.SECONDS.sleep(5);
return 5;
});
Integer result = future.get();
二、Future 模式--回调式(Guava)
Future 模式的第二种用法便是回调。很不幸的事,JDK 实现的 Future 并没有实现 callback, addListener 这样的方法,想要在 JAVA 中体验到 callback 的特性,得引入一些额外的框架。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
ListenableFuture<Integer> future = service.submit(new Callable<Integer>() {
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return 100;
}
});
Futures.addCallback(future, new FutureCallback<Integer>() {
public void onSuccess(Integer result) {
System.out.println("success:" + result);
}
public void onFailure(Throwable throwable) {
System.out.println("fail, e = " + throwable);
}
});
Thread.currentThread().join();
三、Future 模式--回调式(Netty4)
Netty 除了是一个高性能的网络通信框架之外,还对 jdk 的Future 做了扩展,引入 Netty 的 maven 依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.22.Final</version>
</dependency>
EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
io.netty.util.concurrent.Future<Integer> f = group.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return 100;
}
});
f.addListener(new FutureListener<Object>() {
@Override
public void operationComplete(io.netty.util.concurrent.Future<Object> objectFuture) throws Exception {
System.out.println("计算结果::"+objectFuture.get());
}
});
四、由 Callback Hell 引出 Promise 模式
同样的如果你对 ES6 有所接触,就不会对 Promise 这个模式感到陌生,如果你对前端不熟悉,也不要紧,我们先来看看回调地狱(Callback Hell)是个什么概念。
回调是一种我们推崇的异步调用方式,但也会遇到问题,也就是回调的嵌套。当需要多个异步回调一起书写时,就会出现下面的代码(以 js 为例):
asyncFunc1(opt, (...args1) => {
asyncFunc2(opt, (...args2) => {
asyncFunc3(opt, (...args3) => {
asyncFunc4(opt, (...args4) => {
// some operation
});
});
});
});
虽然在 Java 业务代码中很少出现回调的多层嵌套,这样的代码不易读,嵌套太深修改也麻烦。于是 ES6 提出了 Promise 模式来解决回调地狱的问题。可能就会有人想问:Java 中存在 Promise 模式吗?答案是肯定的。
前面提到了 Netty 和 Guava 的扩展都提供了 addListener 这样的接口,用于处理 Callback 调用,但其实 jdk1.8 已经提供了一种更为高级的回调方式:CompletableFuture。首先尝试用 CompletableFuture 来解决回调的问题。
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
TimeUnit.SECONDS.sleep(5);
return 100;
});
completableFuture.whenComplete((result, e) -> {
System.out.println("结果:" + result);
});
Thread.currentThread().join();
五、Future 和 Promise
Netty 文档说明 Netty 的网络操作都是异步的, 在源码上大量使用了 Future/Promise 模型,在 Netty 里面也是这样定义的:
- Future 接口定义了 isSuccess(), isCancellable(), cause() 这些判断异步执行状态的方法。(read-only)
- Promise 接口在 extends Future 的基础上增加了 setSuccess(), setFailure() 来标记任务完成或者失败。(writable)

JDK 的 Future
public interface Future<V> {
// 取消异步操作
boolean cancel(boolean mayInterruptIfRunning);
// 异步操作是否取消
boolean isCancelled();
// 异步操作是否完成,正常终止、异常、取消都是完成
boolean isDone();
// 阻塞直到取得异步操作结果
V get() throws InterruptedException, ExecutionException;
// 同上,但最长阻塞时间为timeout
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Netty 对 JDK 的 Future 进行了扩展
public interface Future<V> extends java.util.concurrent.Future<V> {
// 异步操作完成且正常终止
boolean isSuccess();
// 异步操作是否可以取消
boolean isCancellable();
// 异步操作失败的原因
Throwable cause();
// 添加一个监听者,异步操作完成时回调,类比javascript的回调函数
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
// 阻塞直到异步操作完成
Future<V> await() throws InterruptedException;
// 同上,但异步操作失败时抛出异常
Future<V> sync() throws InterruptedException;
// 非阻塞地返回异步结果,如果尚未完成返回null
V getNow();
}
Netty 的 Promise 对又对 Future 进行了扩展
public interface Promise<V> extends Future<V> {
Promise<V> setSuccess(V result);
boolean trySuccess(V result);
Promise<V> setFailure(Throwable cause);
boolean tryFailure(Throwable cause);
boolean setUncancellable();
}
DefaultChannelPromise 是 ChannelPromise 的实现类,它是实际运行时的 Promoise 实例。
参考:
- 《并发编程 Promise, Future 和 Callback》:https://ifeve.com/promise-future-callback/
每天用心记录一点点。内容也许不重要,但习惯很重要!
并发编程(三)Promise, Future 和 Callback的更多相关文章
- Java并发编程 - Runnbale、Future、Callable 你不知道的那点事(二)
Java并发编程 - Runnbale.Future.Callable 你不知道的那点事(一)大致说明了一下 Runnable.Future.Callable 接口之间的关系,也说明了一些内部常用的方 ...
- Java并发编程 - Runnbale、Future、Callable 你不知道的那点事(一)
从事Java开发已经快两年了,都说Java并发编程比较难,比较重要,关键面试必问,但是在我的日常开发过程中,还真的没有过多的用到过并发编程:这不疫情嘛,周末不能瞎逛,就看看师傅们常说的 Runnabl ...
- Java并发编程三个性质:原子性、可见性、有序性
并发编程 并发程序要正确地执行,必须要保证其具备原子性.可见性以及有序性:只要有一个没有被保证,就有可能会导致程序运行不正确 线程不安全在编译.测试甚至上线使用时,并不一定能发现,因为受到当时的 ...
- 从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例
引入高速缓存概念 在计算机在执行程序时,以指令为单位来执行,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入. 由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这 ...
- 【Java并发编程三】闭锁
1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...
- 深入理解JS异步编程三(promise)
jQuery 原本写一个小动画我们可能是这样的 $('.animateEle').animate({ opacity:'.5' }, 4000,function(){ $('.animateEle2' ...
- 并发编程(三) IO模型
五 IO模型 常用的IO模型有4种: 阻塞IO 非阻塞IO IO多路复用 异步IO 不常用的有: 驱动信号 5.1 阻塞IO.非阻塞IO 阻塞IO:进程不能做其他的事情 非阻塞IO:等待数据无阻塞 阻 ...
- day32_8_14 并发编程三 线程的GIL
一.GIL 什么是GIL? GIL是一个全局排他锁,简单来说就是为了防止多线程并行操作的锁.这里有官方解释: In CPython, the global interpreter lock, or G ...
- Java并发编程 (三) 项目准备
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.案例环境初始化 1.环境搭建与准备 Spring Boot 项目,https://start.spr ...
随机推荐
- 《opencv学习》 之 OTSU算法实现二值化
主要讲解OTSU算法实现图像二值化: 1.统计灰度级图像中每个像素值的个数. 2.计算第一步个数占整个图像的比例. 3.计算每个阈值[0-255]条件下,背景和前景所包含像素值总个数和总概率(就 ...
- 关于tcp的三次握手与四次挥手,以及粘包
tcp三次握手: TCP是因特网中的传输层协议,使用三次握手协议建立连接.当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认.这种建立连接的方法 ...
- idea-activate code
N757JE0KCT-eyJsaWNlbnNlSWQiOiJONzU3SkUwS0NUIiwibGljZW5zZWVOYW1lIjoid3UgYW5qdW4iLCJhc3NpZ25lZU5hbWUiO ...
- uva-10562-二叉树
题意: Homer教授被报道失踪了,我们怀疑这和他最近的研究有关,但是我们确实不知道他最近在研究什么. 侦探们试图侵入他的电脑,再几次失败后才意思到教授的智力超出他们很多............... ...
- 手机端移动端的选择框mobileSelect.js使用
手机端移动端的选择框mobileSelect.js使用 文件地址:https://github.com/onlyhom/mobileSelect.js 请感兴趣的自行下载 使用过程 1 引入标签 &l ...
- checkbox的美化(转)
http://www.w3cfuns.com/blog-5422889-5398674.html <!DOCTYPE html> <html> <head> < ...
- 39. 在linux下装好Tomcat要给 tomcat/bin/下面所有.sh的文件执行权限
chmod a+x *.sh(赋予可执行的权限)
- HTML 表格标签
<table border="1"> <tr> <td>row 1, cell 1</td> <td>row 1, ce ...
- leader 选举机制
from: http://www.jasongj.com/2015/01/02/Kafka%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/ 一种非常常用的选举leader的方 ...
- 【转】Luajit-2.1.0-beta1的发布和生成arm64用bytecode的解脱
来自:Luajit-2.1.0-beta1的发布和生成arm64用bytecode的解脱 前情提要:由于苹果要求2015年2月1日上架的新app必须支持64位的arm64,旧的app也得在6月1日支持 ...