并发是一个难问题,但是通过强有力和简单的抽象,可以简化。为了使问题简化,Guava
使用了JDK 的ListenableFuture
接口。
这里强烈推荐诸位使用ListenableFuture
而不是Future
。原因有如下三点:
Future
方法需要ListenableFuture
ListenableFuture
很容易Future
以及ListenableFuture
他们方法的变体。一个传统的Future
代表着一个异步计算(computation,下文都将用计算指代)的结果,什么样的计算呢?它有可能已经完成了计算并返回结果,也有可能至今还没完成。一个Future
可以是一个内部计算的handle,一个提供我们结果的服务的承诺。
一个ListenableFuture
可以让你注册一个回调函数,一旦计算完毕,就会执行它。或者,这个任务早已经执行完毕,那就立刻执行这个回调函数。ListenableFuture
增加了这一项简单的功能,就可以高效的支持到许多基础的Future
无法支持的操作。
ListenableFuture
的基本操作就是addListener(Runnable, Executor)
方法,它指定了当这个Future
代表的计算执行完成,指定的Runnable
将会被指定的Executor
运行。
大多数的程序员倾向于使用Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor)
,或者是默认使用MoreExecutors.directExecutor()
的版本,callback函数使用起来更加快捷方便。使用FutureCallback<V>
需要你实现两个方法:
根据JDK的 ExecutorService.submit(Callable)
这个方法的返回,可以初始化一个异步的计算future,Guava 提供了ListeningExecutorService
接口,这个接口无论在ExecutorService
的哪里返回一个正常的Future
,都会返回一个ListenableFuture
,将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
来转化的的,你可以选择Guava提供的ListenableFutureTask.create(Callable<V>)
和ListenableFutureTask.create(Runnable, V)
。
使用ListenableFutureTask
最重要的一个理由是,对异步操作可以有复杂的调用链。
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);
https://github.com/google/guava/wiki/ListenableFutureExplained