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/ 有时间了整理一下
随机推荐
- /langversion 的选项“4”无效;必须是 ISO-1、ISO-2、3 或 Default SystemFrameWorkV3
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version Edit th ...
- Oracle18c OnlyExadataVersion 安装说明
1.根据惜分飞还有盖国强老师云和恩墨的文章, 验证了下OnlyExadataVersion的Oracle18c的数据库安装过程. 2.oracle参数修改以及创建用户, 目录, 修改.bash_pro ...
- Excel 使用AutoFill提示“类Range的AutoFill方法无效”
今天遇到一个神奇的问题,之前一直使用很好的代码突然报错:“类Range的AutoFill方法无效”,在网上搜索了一番,感觉没有一个与我遇到的情况相同的.debug,查看一下代码,发现程序里,AutoF ...
- ionic2添加 android平台出现的问题
nodejs版本不宜过高 cordova版本不宜过高 此情况应采取 cordova platform add android --nofetch
- MySQL_基础知识
-----基础知识 1.什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库 2.什么是关系型数据库.主键,外键,索引分别是什么? 关系型数据 ...
- link & auto cards
link & auto cards a link to card link https://docs.embed.ly/docs/cards DD WX <blockquote clas ...
- Delphi学习技巧
我感觉学习最大的诀窍是, 尽快捕捉到设计者(Delphi VCL 的设计者.进而是 Windows 操作系统的设计者)的某些思路, 和大师的思路吻合了, 才能够融汇贯通, 同时从容使用它们的成果. 碰 ...
- Windows 下vim的配置文件_vimrc
set nocompatible source $VIMRUNTIME/vimrc_example.vim source $VIMRUNTIME/mswin.vim behave mswin set ...
- 恕我直言,在座的各位根本写不好Java!
其实,本不想把标题写的那么恐怖,只是发现很多人干了几年 Java 以后,都自认为是一个不错的 Java 程序员了,可以拿着上万的工资都处宣扬自己了,写这篇文章的目的并不是嘲讽和我一样做 Java 的同 ...
- 小整数池和intern机制
在python中,为了优化速度,避免频繁申请和销毁内存空间,python使用小整数池来缓存 range(-5,257) 之间的整数(这里不包含257),这些小整数在赋值引用时使用的都是同一个对象和内存 ...