一. 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 会省事很多。
* 这样工具方法提供者就不需要针对 FutureListenableFuture 都提供方法。

接口

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 接口,这个接口可以返回一个 ListenableFutureExecutorService 只是返回一个普通的 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) 来实现从 FutureListenableFuture 的转换。所以,尽可能地使用 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();
//Set a value to return
sf.set("Success");
//Or set a failure Exception
sf.setException(someException);
通过上面的例子,我们看到,通过create()方法,我们可以创建一个默认的ettableFuture实例,
当我们需要为Future实例设置一个返 回值时,我们可以通过set方法,设置的值就是Future实例在执行成功后将要返回的值;
另外,当我们想要设置一个异常导致Future执行失败,我们 可以通过调用setException方法,我们将给Future实例设置指定的异常返回。
当我们有一个方法返回Future实例时,SettableFuture会显得更有价值,但是已经有了Future的返回值,我们也不需要再去执行异步任 务获取返回值。
https://my.oschina.net/indestiny/blog/219368

google Guava包的ListenableFuture解析的更多相关文章

  1. google Guava包的reflection(反射)解析

    译者:万天慧(武祖) 由于类型擦除,你不能够在运行时传递泛型类对象——你可能想强制转换它们,并假装这些对象是有泛型的,但实际上它们没有. 举个例子: ArrayList<String> s ...

  2. com.google.guava 包解析 ——Google Guava官方教程(中文版)

    全网址           http://ifeve.com/google-guava/ 竹子博客:  http://www.cnblogs.com/peida/archive/2013/06/08/ ...

  3. Google guava cache源码解析1--构建缓存器(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHas ...

  4. 第二章 Google guava cache源码解析1--构建缓存器

    1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) ...

  5. Google guava cache源码解析1--构建缓存器(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 下面介绍在LocalCache(CacheBuilder, CacheLoader)中调用的一些方法: Ca ...

  6. Google guava cache源码解析1--构建缓存器(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. CacheBuilder-->maximumSize(long size)     /**       ...

  7. Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) 原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...

  8. 常用限流算法与Guava RateLimiter源码解析

    在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...

  9. Google Guava

    公司用到了 Joiner  HashMultimap 等  都是属于Google Guava包中的东西 官方文档 http://ifeve.com/google-guava/ 有时间了整理一下

随机推荐

  1. PAT L2-015 互评成绩

    https://pintia.cn/problem-sets/994805046380707840/problems/994805062432309248 学生互评作业的简单规则是这样定的:每个人的作 ...

  2. Spring Boot features - Profiles

    https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html https://w ...

  3. HTML 5 placeHolder

    <html> <body> <input type="text" id="idNum" placeholder="pla ...

  4. [转帖]UML各种图总结-精华

    UML各种图总结-精华 https://www.cnblogs.com/jiangds/p/6596595.html 之前自己以为画图很简单 不需要用心学 现在发现自己一直没有学会一些基础的知识 能力 ...

  5. zabbix 使用问题两个--中文乱码,以及监控ESXi下的虚拟机

    1. 中文乱码 最开始中文显现 长方形不显示文字.解决办法: c:\windows\fonts 下面复制 楷体的字体(字体随意看自己喜欢) 文件名一般为: simkai.ttf 2.将simkai.t ...

  6. Fuck me 忘记改REDO 造成复制用户超级慢

    . 一个用户的测试环境, 想着复制用户进行功能和单点性能测试. 但是用户数据量较大,见图 2. 发现在测试环境里面复制一个用户 大概耗时2小时20min的时间, 测试虚拟机的配置: 最开始注意到awr ...

  7. bat脚本的写法

    当你每次都要输入相同的命令时,可以把这么多命令存为一个批处理,从此以后,只要运行这个批处理,就相当于打了几行.几十行命令.下面以Nginx服务的停止脚本为例写一个bat批处理文件: 1.新建nginx ...

  8. ES6定型数组

    前面的话 定型数组是一种用于处理数值类型(正如其名,不是所有类型)数据的专用数组,最早是在WebGL中使用的,WebGL是OpenGL ES 2.0的移植版,在Web 页面中通过 <canvas ...

  9. Network UVA - 315(求割点)

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  10. 详解基于朴素贝叶斯的情感分析及 Python 实现

    相对于「 基于词典的分析 」,「 基于机器学习 」的就不需要大量标注的词典,但是需要大量标记的数据,比如: 还是下面这句话,如果它的标签是: 服务质量 - 中 (共有三个级别,好.中.差) ╮(╯-╰ ...