本文是對 Guava 中 ListenableFuture 的學習介紹。歡迎加入學習項目: LearningGuava肥惭。
以下參考: 官方文檔
處理并發(fā)是一個很困難的問題盯仪,但是我們可以通過使用功能強大的抽象來簡化這個工作。為了簡化這個問題蜜葱,Guava 提供了 ListenableFuture全景,它繼承了 JDK 中的 Future
接口。
我們強烈建議:在你的代碼中牵囤,使用 ListenableFuture
來替代 Future
爸黄,因為
- 很多
Future
相關的方法需要它。 - 一開始就使用
ListenableFuture
會省事很多揭鳞。 - 這樣工具方法提供者就不需要針對
Future
和ListenableFuture
都提供方法炕贵。
接口
Future
代表了異步執(zhí)行的結果:一個可能還沒有產(chǎn)生結果的執(zhí)行過程。 Future
可以正在被執(zhí)行野崇,但是會保證返回一個結果称开。
ListenableFuture
可以使你注冊回調(diào)函數(shù),使得在結果計算完成的時候可以回調(diào)你的函數(shù)乓梨。如果結果已經(jīng)算好鳖轰,那么將會立即回調(diào)。這個簡單的功能使得可以完成很多 Future
支持不了的操作扶镀。
ListenableFuture
添加的基本函數(shù)是 addListener(Runnable, Executor)
蕴侣。通過這個函數(shù),當 Future
中的結果執(zhí)行完成時臭觉,傳入的 Runnable
會在傳入的 Executor
中執(zhí)行昆雀。
添加回調(diào)函數(shù)
使用者偏向于使用 Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor)
, 或者當需要注冊輕量級的回調(diào)的時候,可以使用默認為 MoreExecutors.directExecutor()
的版本蝠筑。
FutureCallback<V>
實現(xiàn)了兩個方法:
-
onSuccess(V)
:當 future 執(zhí)行成功時候的反應忆肾。 -
onFailure(Throwable)
:當 future 執(zhí)行失敗時候的反應。
創(chuàng)建
與 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 值的抽象,而不是實現(xiàn)一個方法來計算結果亦鳞,那么可以考慮直接擴展 AbstractFuture<V>
或者 SettableFuture
馍忽。
如果你一定要將一個基于 Future
的 API 轉換為基于 ListenableFuture
的話,你不得不采用硬編碼的方式 JdkFutureAdapters.listenInPoolThread(Future)
來實現(xiàn)從 Future
到 ListenableFuture
的轉換燕差。所以遭笋,盡可能地使用 ListenableFuture
。
應用
使用 ListenableFuture
一個最重要的原因就是:可以基于他實現(xiàn)負責的異步執(zhí)行鏈徒探。如下所示:
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
被高效地支持瓦呼。不同的操作可能被不同的執(zhí)行器執(zhí)行,而且一個 ListenableFuture
可以有多個響應操作测暗。
當 ListenableFuture
有多個后續(xù)操作的時候央串,這樣的操作稱為:“扇出”。當它依賴多個輸入 future 同時完成時碗啄,稱作“扇入”质和。可以參考 Futures.allAsList
的實現(xiàn)稚字。
方法 | 描述 | 參考 |
---|---|---|
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 的成功執(zhí)行值的按序列表尉共,對于取消或者失敗的任務褒傅,對應的值是 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 的取消操作不能傳遞到內(nèi)層的 future鸠按。此外,一個常犯的錯誤是:使用 get()
或者 listener 來檢測其它 future 的失敗饶碘。為了避免這樣的情況目尖,Guava 所有處理 future 的方法(以及一些來自 JDK 的代碼)具有安全解決嵌套的版本。
CheckedFuture
Guava 也提供 CheckedFuture<V, X extends Exception>
接口扎运。
CheckedFuture
是這樣的一個 ListenableFuture
:具有多個可以拋出受保護異常的 get
方法瑟曲。這使得創(chuàng)建一個執(zhí)行邏輯可能拋出異常的 future 變得容易饮戳。使用 Futures.makeChecked(ListenableFuture<V>, Function<Exception, X>)
可以將 ListenableFuture
轉換為 CheckedFuture
。