ListenableFutureExplained
併發是一個困難的問題,但是使用強大而簡單的抽象可以極大地簡化併發問題。為了簡化事情,Guava使用ListenableFuture
繼承了JDK的Future介面.
我們強烈建議你在所在程式碼裡總是使用ListenableFuture,而不是Future,因為:
- 大多數Future相關的介面需要它
- 這比以後換成ListenableFuture更容易
- 工具的提供者不需要為它們的方法分別提供Future和ListenableFuture的變體
介面 Interface
一個傳統的Future代表一個非同步計算的結果:一個可能已經得到結果或者還沒結束的計算。一個Future可以作為一個正在進行中的計算的控制程式碼(handle), 作為一個服務對我們的保證(promise),保證它會提供給我們一個結果。
一個ListenableFuture允許我們註冊一個在計算完成時會執行的回撥(callback),如果計算已經完成,就會立即執行回撥。這個增加的簡單功能可以讓ListenableFuture支援很多基本的Future介面所不支援的操作。
ListenableFuture所增加的基本操作是addListener(Runnable, Executor)
, 它指明當這個Future代表的計算完成時,特定的Runnable將會在特定的Executor中執行。
填加回撥 Adding Callbacks
大多數使用者傾向於使用Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor),或者另一個預設使用MoreExecutors.sameThreadExecutor的版本,來在回撥執行快速並且輕量的時候使用。一個需要實現兩個方法
FutureCallback<V>
:
onSuccess(V)
,在future成功時執行的動作,基於future的結果。onFailure(Throwable),
當future失敗時執行的動作,基於失敗的原因。
建立 Creation
與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
。
如果你必須把其它API提供的Future轉成ListenableFuture,你可能別無選擇,而必須使用重量級 JdkFutureAdapters.listenInPoolThread(Future)
的來把一個Future轉成ListenableFuture. 只要情況允許,更好的選擇就是修改原來的程式碼,讓它返回一個ListenableFuture。
應用 Application
(譯註:這個application應該理解成apply的名詞,是'apply a function to a value'裡的apply的意思, 請從函數語言程式設計的角度理解)
使用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.transform(rowKeyFuture, queryFunction, queryExecutor);
(譯註:如果懂Scala的話,這段程式碼大體相當於rowKeyFuture.map(dataService.read)(queryExecutor), 要短了非常多,也更好理解。但是Scala的Future好像不像ListenableFuture可以註冊多個監聽器)
有很多其它的操作也可以被ListenableFuture高效的支援,但是Future就不行。不同的操作可以在不同的executor中執行,一個ListenableFuture也可以有多個等待執行的動作。
當很多個操作都要在另一個操作開始後立即執行--也就是扇出(fan-out)--ListenableFuture可以做到這點:它可以觸發所有那些的需要執行的回撥。多做一點工作,我們就可以扇入(fan-in),或者觸發一個ListenableFuture,在其它所有的Future都完成以後,它會立即執行: 一個例子是the implementation of Futures.allAsList
Method | Description | See also |
---|---|---|
transform(ListenableFuture<A>, AsyncFunction<A, B>, Executor) * |
Returns a newListenableFuture whose result is the product of applying the givenAsyncFunction to the result of the givenListenableFuture . |
transform(ListenableFuture<A>, AsyncFunction<A, B>) |
transform(ListenableFuture<A>, Function<A, B>, Executor) |
Returns a newListenableFuture whose result is the product of applying the given Function to the result of the givenListenableFuture . |
transform(ListenableFuture<A>, Function<A, B>) |
allAsList(Iterable<ListenableFuture<V>>) |
Returns aListenableFuture whose value is a list containing the values of each of the input futures, in order. If any of the input futures fails or is cancelled, this future fails or is cancelled. |
allAsList(ListenableFuture<V>...) |
successfulAsList(Iterable<ListenableFuture<V>>) |
Returns aListenableFuture whose value is a list containing the values of each of the successful input futures, in order. The values corresponding to failed or cancelled futures are replaced with null . |
successfulAsList(ListenableFuture<V>...) |
Method 方法 | Description 描述 | See also 參見 |
---|---|---|
transform(ListenableFuture<A>, AsyncFunction<A, B>, Executor) * |
Returns a new 返回一個新的ListenableFuture, 它的結果是把給定的ListenableFuture的結果應用於給定的非同步函式的結果(譯註:意思是把給定的ListenableResult的result作為引數呼叫給定的非同步函式) |
transform(ListenableFuture<A>, AsyncFunction<A, B>) |
transform(ListenableFuture<A>, Function<A, B>, Executor) |
Returns a new 返回一個新的ListenableFuture,它的結果是把給定的ListenableFuture的結果應用於給定的函式的結果 |
transform(ListenableFuture<A>, Function<A, B>) |
allAsList(Iterable<ListenableFuture<V>>) |
Returns a 返回一個新的ListenableFuture,它的結果一個list, 這個list包括了給定的一系列ListenableFuture的結果,list的元素按照這些給定的ListenableFuture的順序。如果給定的這些futures中有任何一個被取消了或者失敗了,被返回的這個ListenableFuture就會被取消或失敗。 |
allAsList(ListenableFuture<V>...) |
successfulAsList(Iterable<ListenableFuture<V>>) |
Returns a 返回一個新的ListenableFuture,它的值是一個list,這個list包括了每一個成功執行的future,按照給出的順序。跟失敗或者被取消的future相關的值會被設成null |
successfulAsList(ListenableFuture<V>...) |
*
An AsyncFunction<A, B>
provides one method, ListenableFuture<B> apply(A input)
. It can be used to asynchronously transform a value.
一個提供了一個方法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);
CheckedFuture
Guava也提供了一個CheckedFuture<V, X extends Exception>
介面。一個CheckedFuture 也是一個ListenableFuture,包括了各種版本的可以丟擲受檢異常的方法。這使得建立一個執行邏輯可能丟擲異常的Future更加容易。如果想要把一個ListenableFuture轉成一個CheckedFuture, 可以使用Futures.makeChecked(ListenableFuture<V>, Function<Exception, X>)
.
總結:
ListenableFuture提供了很有價值的對JDK併發庫的補充。不過同樣的功能,Scala提供的Future的功能要更強大,而且Scala的語法也使得程式碼更簡單易懂。實際上,Promise也是很強大的一種抽象,這篇Guava的文章只簡單提了一下SettableFuture。想要更深入瞭解的可以看一下Scala的文件,這個有人翻譯了,頁面的右上角可以選擇中文版。
Futures and Promises