并发编程(三)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 ...
随机推荐
- django的小操作,查询效率up, 引用art-template模板+djangorestframework
Part1: 提高查询效率newses = News.objects.select_related('category', 'author').get(id=1) # category和author字 ...
- springsource-tool-suite插件各个历史版本
转自:https://blog.csdn.net/zhen_6137/article/details/79384798 目前spring官网(http://spring.io/tools/sts/al ...
- Socket Error # 10013 Access denied
--------------------------- Debugger Exception Notification --------------------------- Project xxx. ...
- XE 创建 Active Form
XE6: http://docwiki.embarcadero.com/RADStudio/XE6/en/Generating_an_Active_Form_Based_on_a_VCL_Form h ...
- Spring Boot 入门搭建
一.前言 Spring Boot 的设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 二.环境搭建 创建一个 ...
- rssh RSA(非对称密钥)
rssh ,非对称密钥,分为密钥和公钥 ,密钥在对面机器,需要进入的文件中,公钥是放在本地机器上 import paramiko private_key = paramiko.RSAKey.from_ ...
- 8 并发编程-(线程)-多线程与多进程的区别&Thread对象的其他属性或方法
1.开启速度 在主进程下开启线程比 开启子进程快 # 1 在 主进程下开启线程 from threading import Thread def work(): print('hello') if ...
- UI5-文档-4.33-Routing Back and History
现在我们可以导航到细节页面并显示发票,但是还不能回到概览页面.我们将向细节页面添加一个back按钮,并实现一个函数,再次显示概述页面. Preview A back button is now dis ...
- 常用类一一字符串相关类一一String类 字符串的使用
Java字符串就是Unicode字符序列,例如“Java”就是4个Unicode字符J,a,v,a组成的. Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个 ...
- pandas 一行文本拆多行,一列拆多列
https://zhuanlan.zhihu.com/p/28337202 一列拆多列: http://blog.csdn.net/qq_22238533/article/details/761875 ...