google Guava包的ListenableFuture解析
一. ListenableFuture是用来增强Future的功能的。
我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,不断代码复杂,而且效率低下。
ListenableFuture,顾名思义,就是可以监听的Future。我们可以为ListenableFuture增加Listener监听器,当任务完成时,直接执行某个线程,或者我们可以直接为ListenableFuture设置回调函数,当任务完成时,自动执行该回调方法。
Future模式的缺点
Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。
1.要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。2.要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
通过Future接口的get()方法可以获取类Callable的返回值,但是此方法最大缺点是阻塞的(用户态到核心态,开销大),因此在并发环境下,效率比较低。Google公司提供的开源Guava库提供了有效的处理异步Future的问题。
下面我们看看ListenableFuture的实际用法。
(1)怎样得到ListenableFuture对象?
我们知道ExecutorService.submit(Callable) 会返回Future对象,那么ListeningExecutorService.submit(Callable)会返回ListenableFuture对象。
ListeningExecutorService可以通过MoreExecutors.listeningDecorator(ExecutorService)来得到。
(2)增加Listenable功能
方法一:直接添加监听
ListenableFuture. addListener(Runnable listener, Executor executor)
可以为ListenableFuture增加一个监听,当线程计算完成时,自动在executor中执行一个Runnable线程。
方法二:添加回调方法
Futures.addCallback(ListenableFuture, new FutureCallback<T>() {
public void onSuccess(T t) {
}
public void onFailure(Throwable thrown) {
}
});
Futures的静态方法addCallback可以为ListenableFuture对象添加回调函数,回调里面可以定义计算成功时和失败时分别的操作。onSuccess里面可以把计算结果做为参数传入。onFailure里面可以把异常做为参数传入。
那我们一般应该怎么选择这两种方式呢?建议直接用第二种,因为这种方式可以把计算结果做为参数传入。其实,第二种的内部实现就是用的第一种方式,但是用起来会更加的简洁。
https://blog.csdn.net/u010738033/article/details/79633252
以下参考: 官方文档
处理并发是一个很困难的问题,但是我们可以通过使用功能强大的抽象来简化这个工作。为了简化这个问题,Guava 提供了 ListenableFuture,它继承了 JDK 中的 Future 接口。
我们强烈建议:在你的代码中,使用 ListenableFuture 来替代 Future,因为
* 很多 Future 相关的方法需要它。
* 一开始就使用 ListenableFuture 会省事很多。
* 这样工具方法提供者就不需要针对 Future 和 ListenableFuture 都提供方法。
接口
Future 代表了异步执行的结果:一个可能还没有产生结果的执行过程。 Future 可以正在被执行,但是会保证返回一个结果。
ListenableFuture 可以使你注册回调函数,使得在结果计算完成的时候可以回调你的函数。如果结果已经算好,那么将会立即回调。这个简单的功能使得可以完成很多 Future 支持不了的操作。
ListenableFuture 添加的基本函数是 addListener(Runnable, Executor)。通过这个函数,当 Future 中的结果执行完成时,传入的 Runnable 会在传入的 Executor 中执行。
添加回调函数
使用者偏向于使用 Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor) , 或者当需要注册轻量级的回调的时候,可以使用默认为 MoreExecutors.directExecutor() 的版本。
FutureCallback<V> 实现了两个方法:
* onSuccess(V) :当 future 执行成功时候的反应。
* onFailure(Throwable):当 future 执行失败时候的反应。
创建
与 JDK 中 通过 ExecutorService.submit(Callable) 来初始化一个异步的任务相似,Guava 提供了一个 ListeningExecutorService 接口,这个接口可以返回一个 ListenableFuture(ExecutorService 只是返回一个普通的 Future)。如果需要将一个 ExecutorService 转换为 ListeningExecutorService,可以使用 MoreExecutors.listeningDecorator(ExecutorService)。一个使用示例如下:
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Explosion> explosion = service.submit(new Callable<Explosion>() {
public Explosion call() {
return pushBigRedButton();
}
});
Futures.addCallback(explosion, new FutureCallback<Explosion>() {
// we want this handler to run immediately after we push the big red button!
public void onSuccess(Explosion explosion) {
walkAwayFrom(explosion);
}
public void onFailure(Throwable thrown) {
battleArchNemesis(); // escaped the explosion!
}
});
如果你想从一个基于 FutureTask 的 API 转换过来,Guava 提供了 ListenableFutureTask.create(Callable<V>) 和 ListenableFutureTask.create(Runnable, V)。和 JDK 不一样,ListenableFutureTask 并不意味着可以直接扩展。
如果你更喜欢可以设置 future 值的抽象,而不是实现一个方法来计算结果,那么可以考虑直接扩展 AbstractFuture<V> 或者 SettableFuture。
如果你一定要将一个基于 Future 的 API 转换为基于 ListenableFuture 的话,你不得不采用硬编码的方式 JdkFutureAdapters.listenInPoolThread(Future) 来实现从 Future 到 ListenableFuture 的转换。所以,尽可能地使用 ListenableFuture。
应用
使用 ListenableFuture 一个最重要的原因就是:可以基于他实现负责的异步执行链。如下所示:
ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query);
AsyncFunction<RowKey, QueryResult> queryFunction =
new AsyncFunction<RowKey, QueryResult>() {
public ListenableFuture<QueryResult> apply(RowKey rowKey) {
return dataService.read(rowKey);
}
};
ListenableFuture<QueryResult> queryFuture =
Futures.transformAsync(rowKeyFuture, queryFunction, queryExecutor);
很多不能被 Future 支持的方法可以通过 ListenableFuture 被高效地支持。不同的操作可能被不同的执行器执行,而且一个 ListenableFuture 可以有多个响应操作。
当 ListenableFuture 有多个后续操作的时候,这样的操作称为:“扇出”。当它依赖多个输入 future 同时完成时,称作“扇入”。可以参考 Futures.allAsList的实现。
| 方法 | 描述 | 参考 |
|---|---|---|
transformAsync(ListenableFuture<A>, AsyncFunction<A, B>, Executor) |
返回新的 ListenableFuture,它是给定 AsyncFunction 结合的结果 |
transformAsync(ListenableFuture<A>, AsyncFunction<A, B>) |
transform(ListenableFuture<A>, Function<A, B>, Executor) |
返回新的 ListenableFuture,它是给定 Function 结合的结果 |
transform(ListenableFuture<A>, Function<A, B>) |
allAsList(Iterable<ListenableFuture<V>>) |
返回一个 ListenableFuture,它的值是一个输入 futures 的值的按序列表,任何一个 future 的失败都会导致最后结果的失败 |
allAsList(ListenableFuture<V>...) |
successfulAsList(Iterable<ListenableFuture<V>>) |
返回一个 ListenableFuture,它的值是一个输入 futures 的成功执行值的按序列表,对于取消或者失败的任务,对应的值是 null |
successfulAsList(ListenableFuture<V>...) |
AsyncFunction<A, B> 提供了一个方法:ListenableFuture<B> apply(A input)。可以被用来异步转换一个值。
List<ListenableFuture<QueryResult>> queries;
// The queries go to all different data centers, but we want to wait until they're all done or failed.
ListenableFuture<List<QueryResult>> successfulQueries = Futures.successfulAsList(queries);
Futures.addCallback(successfulQueries, callbackOnSuccessfulQueries);
避免嵌套 Future
在使用通用接口返回 Future 的代码中,很有可能会嵌套 Future。例如:
executorService.submit(new Callable<ListenableFuture<Foo>() {
@Override
public ListenableFuture<Foo> call() {
return otherExecutorService.submit(otherCallable);
}
});
上述代码将会返回:ListenableFuture<ListenableFuture<Foo>>。这样的代码是不正确的,因为外层 future 的取消操作不能传递到内层的 future。此外,一个常犯的错误是:使用 get() 或者 listener 来检测其它 future 的失败。为了避免这样的情况,Guava 所有处理 future 的方法(以及一些来自 JDK 的代码)具有安全解决嵌套的版本。
CheckedFuture
Guava 也提供 CheckedFuture<V, X extends Exception> 接口。
CheckedFuture 是这样的一个 ListenableFuture:具有多个可以抛出受保护异常的 get 方法。这使得创建一个执行逻辑可能抛出异常的 future 变得容易。使用 Futures.makeChecked(ListenableFuture<V>, Function<Exception, X>)可以将 ListenableFuture 转换为 CheckedFuture。
ListenableFuture类
- jdk5之后有了Future这种异步执行的结构
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> future = executor.submit(new Callable<Integer>(){
public Integer call() throws Exception{
return service.getCount();
} });
//Retrieve the value of computation
Integer count = future.get();
- ListenableFuture对Future进行了扩展,允许注册一个回调函数,task执行完后自动调用。
- 获取ListableFuture对象。
正如我们获取Future对象要通过ExecutorService.submit(Callable)来获取一样,我们可以这样创建ListenableFuture对象:
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(NUM_THREADS)); //包装Executors创建的线程池
ListenableFuture<String> listenableFuture = executorService.submit(new Callable<String>()...); //获取ListableFuture对象
listenableFuture.addListener(new Runnable() {
@Override
public void run() {
methodToRunOnFutureTaskCompletion();
}
}, executorService); //注册回调函数
FutureCallback类
- FutureCallback定义了onSuccess和onFailure方法,onSuccess方法会接收一个Future对象,这样我们就可以获取Future的结果。
- 首先需要一个FutureCallback实现类。
/**
* 定义一个FutureCallBack实现类
*/
public class FutureCallbackImpl implements FutureCallback<String> {
private StringBuilder builder = new StringBuilder();
@Override
public void onSuccess(String result) {
builder.append(result).append(" successfully");
}
@Override
public void onFailure(Throwable t) {
builder.append(t.toString());
}
public String getCallbackResult() {
return builder.toString();
}
}
使用实例:
ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
ListenableFuture<String> futureTask = executorService.submit(new Callable<String>() { //创建ListenaleFuture对象
@Override
public String call() throws Exception {
return "Task completed";
}
});
FutureCallbackImpl callback = new FutureCallbackImpl();
Futures.addCallback(futureTask, callback); //添加回调
callback.getCallbackResult(); //获取结果
如果CallBack是一个耗时操作,你应该选择另一个注册CallBack:
Futures.addCallback(futureTask,callback,executorService); //提供另一个线程池来执行性回调
SettableFuture类:
SettableFuture:不需要实现一个方法来计算返回值,而只需要返回一个固定值来做为返回值,可以通过程序设置此Future的返回值或者异常信息
SettableFuture可以用来设置要返回得值:
SettableFuture<String> sf = SettableFuture.create();通过上面的例子,我们看到,通过create()方法,我们可以创建一个默认的ettableFuture实例,
//Set a value to return
sf.set("Success");
//Or set a failure Exception
sf.setException(someException);
当我们需要为Future实例设置一个返 回值时,我们可以通过set方法,设置的值就是Future实例在执行成功后将要返回的值;
另外,当我们想要设置一个异常导致Future执行失败,我们 可以通过调用setException方法,我们将给Future实例设置指定的异常返回。
当我们有一个方法返回Future实例时,SettableFuture会显得更有价值,但是已经有了Future的返回值,我们也不需要再去执行异步任 务获取返回值。
https://my.oschina.net/indestiny/blog/219368
google Guava包的ListenableFuture解析的更多相关文章
- google Guava包的reflection(反射)解析
译者:万天慧(武祖) 由于类型擦除,你不能够在运行时传递泛型类对象——你可能想强制转换它们,并假装这些对象是有泛型的,但实际上它们没有. 举个例子: ArrayList<String> s ...
- com.google.guava 包解析 ——Google Guava官方教程(中文版)
全网址 http://ifeve.com/google-guava/ 竹子博客: http://www.cnblogs.com/peida/archive/2013/06/08/ ...
- Google guava cache源码解析1--构建缓存器(1)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHas ...
- 第二章 Google guava cache源码解析1--构建缓存器
1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) ...
- Google guava cache源码解析1--构建缓存器(3)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 下面介绍在LocalCache(CacheBuilder, CacheLoader)中调用的一些方法: Ca ...
- Google guava cache源码解析1--构建缓存器(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. CacheBuilder-->maximumSize(long size) /** ...
- Google Guava官方教程(中文版)
Google Guava官方教程(中文版) 原文链接 译文链接 译者: 沈义扬,罗立树,何一昕,武祖 校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...
- 常用限流算法与Guava RateLimiter源码解析
在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...
- Google Guava
公司用到了 Joiner HashMultimap 等 都是属于Google Guava包中的东西 官方文档 http://ifeve.com/google-guava/ 有时间了整理一下
随机推荐
- shell脚本--输入与输出
输出带有转义字符的内容 单独一个echo表示一个换行 使用echo输出时,每一条命令之后,都默认加一个换行:要想取消默认的换行,需要加 -n 参数. #!/bin/bash #文件名:test.sh ...
- CentOS下使用VirtualBox 安装 Windows虚拟机的简单方法
1.物理服务器安装CentOS7.5 2. 安装VNC 3. 关闭防火墙,关闭selinux,上传virtualbox的rpm包. http://download.virtualbox.org/vir ...
- SSM三大框架整合配置(Spring+SpringMVC+MyBatis)
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...
- vue实现带规格商品的表格编辑
实现效果: 需求分析: 商品分为 启用规格 和 未启用规格 两种状态, 启用时显示带规格表格, 不启用时显示无规格价格 规格大项最多添加两个, 比如 "颜色", "尺寸& ...
- postgres(pgAdmin) 客户端保存密码
pgAdmin 大象客户端保存密码后连接服务器,删除掉当前连接,建立一个新的连接不用输入密码也能连接上,其实是客户端保存了密码,让人误以为是空密码可登录.可以通过右键连接,选择重载服务配置,再次连接就 ...
- golang自定义struct字段标签
原文链接: https://sosedoff.com/2016/07/16/golang-struct-tags.html struct是golang中最常使用的变量类型之一,几乎每个地方都有使用,从 ...
- BZOJ2017[USACO 2009 Nov Silver 1.A Coin Game]——DP+博弈论
题目描述 农夫约翰的奶牛喜欢玩硬币游戏,因此他发明了一种称为“Xoinc”的两人硬币游戏. 初始时,一个有N(5 <= N <= 2,000)枚硬币的堆栈放在地上,从堆顶数起的第I枚硬币的 ...
- 【Linux】Centos6.8下一键安装Lamp环境
[下载地址] 以下三种都是快捷安装环境的工具,都提供相应的脚本,原理都相同,一个会了其他的也就都会了,我用的比较多的会是lnmp和oneinstack,最近在用的都是oneinstack,挺好用的. ...
- Dumb Bones UVA - 10529(概率dp)
题意: 你试图把一些多米诺骨牌排成直线,然后推倒它们.但是如果你在放骨牌的时候不小心把刚放的骨牌碰倒了,它就会把相临的一串骨牌全都碰倒, 而你的工作也被部分的破坏了. 比如你已经把骨牌摆成了DD__D ...
- json序列化 & 反序列化
json序列化: json的dumps方法可以将json格式数据序列为python的相关数据类型,比如str,常用于打印,另外,在序列化时,中文汉字被转换为unicode编码,在dumps函数中添加参 ...