Guava中的ListenableFuture
試圖為Future
對象定義一致的API,以注冊完成回調。 通過在Future
完成時添加回調的功能,我們可以異步有效地響應傳入的事件。 如果您的應用程序與許多將來的對象高度并發,我強烈建議您盡可能使用ListenableFuture
。 從技術上講, ListenableFuture
通過添加以下簡單ListenableFuture
擴展了Future
接口:
void addListener(Runnable listener, Executor executor)
方法。 而已。 如果掌握了ListenableFuture
,則可以注冊Runnable
以在有問題的將來完成時立即執行。 您還必須提供將用于執行您的偵聽Executor
( ExecutorService
擴展)–這樣長時間運行的偵聽器不會占用您的工作線程。
讓我們付諸行動。 我們將從重構我們的第一個Web ListenableFuture
器示例以使用ListenableFuture
。 幸運的是,在線程池的情況下,只需使用MoreExecutors.listeningDecorator()
將它們包裝起來MoreExecutors.listeningDecorator()
:
ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));for (final URL siteUrl : topSites) {final ListenableFuture<String> future = pool.submit(new Callable<String>() {@Overridepublic String call() throws Exception {return IOUtils.toString(siteUrl, StandardCharsets.UTF_8);}});future.addListener(new Runnable() {@Overridepublic void run() {try {final String contents = future.get();//...process web site contents} catch (InterruptedException e) {log.error("Interrupted", e);} catch (ExecutionException e) {log.error("Exception in task", e.getCause());}}}, MoreExecutors.sameThreadExecutor());
}
有幾個有趣的發現。 首先,請注意ListeningExecutorService
如何包裝現有的Executor
。 這類似于ExecutorCompletionService
方法 。 稍后,我們注冊自定義Runnable
,以便在每個任務完成時得到通知。 其次,請注意錯誤處理變得多么丑陋:我們必須處理InterruptedException
(從技術上講,這應該永遠不會發生,因為Future
已經解決,并且get()
永遠不會拋出它)和ExecutionException
。 我們還沒有介紹,但是Future<T>
必須以某種方式處理異步計算期間發生的異常。 此類異常包含在從get()
引發的ExecutionException
(因此在記錄期間調用getCause()
get()
。
最后請注意正在使用MoreExecutors.sameThreadExecutor()
。 這是一個方便的抽象,您可以使用每API一些想要使用的時間Executor
/ ExecutorService
(大概線程池),而你的罰款使用當前線程。 這在單元測試期間特別有用–即使您的生產代碼使用異步任務,在測試期間您也可以從同一線程運行所有內容。
不管它多么方便,整個代碼看起來都有些混亂。 幸運的是,在夢幻般的Futures
類中有一個簡單的實用程序方法:
Futures.addCallback(future, new FutureCallback<String>() {@Overridepublic void onSuccess(String contents) {//...process web site contents}@Overridepublic void onFailure(Throwable throwable) {log.error("Exception in task", throwable);}
});
FutureCallback
是一個更簡單的抽象方法,可以解決將來的問題并為您執行異常處理。 如果需要,您仍然可以為偵聽器提供自定義線程池。 如果您仍然使用一些仍會返回Future
舊式API,則可以嘗試JdkFutureAdapters.listenInPoolThread()
,它是將普通Future<V>
轉換為ListenableFuture<V>
的適配器。 但是請記住,一旦開始使用addListener()
,每個這樣的適配器將只需要一個線程即可工作,因此此解決方案根本無法擴展,因此應避免使用它。
Future<String> future = //...
ListenableFuture<String> listenableFuture =JdkFutureAdapters.listenInPoolThread(future);
一旦了解了基礎知識,我們就可以深入探究聽期貨的最大優勢: 轉型和連鎖 。 這是高級材料,已被警告。
參考: NoBlogDefFound博客中來自JCG合作伙伴 Tomasz Nurkiewicz的番石榴中的ListenableFuture 。
翻譯自: https://www.javacodegeeks.com/2013/02/listenablefuture-in-guava.html