這篇文章是我在Google Guava上的系列文章的延續,這次涵蓋了Future。 Futures類是用于使用Future / ListenableFuture接口的靜態實用程序方法的集合。 Future是提交給ExecutorService的異步任務(可運行或可調用)的句柄。 Future界面提供以下方法:獲取任務的結果,檢查任務是否完成或取消任務。 ListenableFuture接口擴展了Future接口,并添加了將完成偵聽器設置為在任務完成后運行的功能。 要創建ListenableFuture,您首先需要裝飾一個ExecutorService實例,如下所示:
ExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
現在所有提交的Callables / Runnables將返回一個ListenableFuture。 MoreExecutors可以在com.google.common.util.concurrent包中找到。 ListenableFutures在覆蓋以前的帖子 。 期貨中有太多方法無法有效地涵蓋在一篇文章中,所以我只涉及:鏈,轉換,allAsList和successAsList。 在這篇文章中,我將交替使用Futures和ListenableFutures。
鏈
鏈方法返回一個ListenableFuture,其值是通過從輸入Future中獲取結果并將其作為參數應用到Function對象來計算的, Function對象又返回另一個ListenableFuture。 讓我們看一個代碼示例并逐步執行:
ListenableFuture<List<String>> indexSearch = luceneSearcher.searchAsync('firstName:martin');Function<List<String>, ListenableFuture<List<Person>>> queryFunction = new Function<List<String>, ListenableFuture<List<Person>>>() {@Overridepublic ListenableFuture<List<Person>> apply(final List<String> ids) {return dataService.getPersonsByIdAsync(ids);}};ListenableFuture<List<Person>> results = Futures.chain(indexSearch, queryFunction,executorService);
- 第1行正在使用Lucene執行異步搜索,并將返回一個ID列表,這些ID代表存儲在數據庫中的人員記錄的主鍵。 (我創建了一個小索引,其中存儲在Lucene中的唯一數據是id的數據,其余數據僅被索引了)。
- 第4 – 11行正在構建功能對象,其中apply方法將使用搜索未來的結果作為輸入。 apply返回的將來是對dataService對象的調用的結果。
- 第12行是從鏈調用返回的未來。 一旦輸入將來完成,將使用executorService運行該功能。
為了更加清楚,這是searchAsync和getPersonsByIdAsync方法的作用。 在前面的代碼示例中,這些方法調用分別來自第2行和第8行:
public ListenableFuture<List<String>> searchAsync(final String query) {return executorService.submit(new Callable<List<String>>() {@Overridepublic List<String> call() throws Exception {return search(query);}});}public ListenableFuture<List<Person>> getPersonsByIdAsync(final List<String> ids) {return executorService.submit(new Callable<List<Person>>() {@Overridepublic List<Person> call() throws Exception {return getPersonsById(ids);}});}
chain方法具有兩個簽名:
- 鏈(ListentableFuture,函數)
- 鏈(ListenableFuture,函數,ExecutorService)
在確定使用哪種方法時,有幾點要考慮。
如果通過調用時間鏈完成了輸入將來,則所提供的函數將在調用線程中立即執行。 此外,如果未提供執行程序,則使用MoreExecutors.sameThreadExecutor。 MoreExecutors.sameThreadExecutor(顧名思義)位于ThreadPoolExecutor.CallerRunsPolicy之后,這意味著提交的任務在與執行/提交相同的線程中運行。
轉變
轉換方法類似于鏈式方法,因為它以Future和Function對象作為參數。 不同之處在于,不返回ListenableFuture,僅返回將給定功能應用于輸入future的結果。 考慮以下:
List<String> ids = ....
ListenableFuture<List<Map<String, String>>> dbRecords = dataService.getPersonDataByIdAsync(ids);Function<List<Map<String, String>>,List<Person>> transformDbResults = new Function<List<String>, List<Person>>() {@Overridepublic List<Person> apply(List<Map<String, String>> personMapList) {List<Person> personObjList = new ArrayList<Person>();for(Map<String,String> personDataMap : personMapList){personObjList.add(new Person(personDataMap);} return personObjList;}};ListenableFuture<List<Person>> transformedResults = Futures.transform(dbRecords, transformDbResults, executorService);
- 在第2行上,執行異步數據庫查找
- 在第4行上,正在創建一個函數對象,但是在第8行上,請注意返回類型為List <Person>
transform方法具有與chain相同的重載方法調用,但有相同的警告。
AllAsList
allAsList方法將采用任意數量的ListenableFutures作為變量或以Iterator <ListenableFuture>的形式。 返回一個ListenableFuture,其值是所有輸入結果的列表。 列表中返回的值與原始列表的順序相同。 如果任何輸入值被取消或失敗,則返回的ListenableFuture也將被取消或失敗。 從allAsList調用取消返回的future不會傳播到列表中提交的任何原始任務。
ListenableFuture<List<Person>> lf1 = getPersonsByFirstNameFuture('martin');
ListenableFuture<List<Person>> lf2 = getPersonsByFirstNameFuture('bob');
ListenableFuture<List<List<Person>>> lfResults = Futures.allAsList(lf1, lf2);
//assume lf1 failed
List<List<Person>> personLists = lfResults.get() //call results in exception
成功名單
successAsList方法與allAsList非常相似,但是更加寬容。 就像allAsList一樣,successAsList返回結果列表的順序與輸入列表的順序相同,但是如果任何輸入失敗或被取消,則列表中的相應值將為null。 取消返回的將來也不會取消任何原始輸入。
ListenableFuture<List<Person>> lf1 = getPersonsByFirstNameFuture('martin');
ListenableFuture<List<Person>> lf2 = getPersonsByFirstNameFuture('bob');
ListenableFuture<List<List<Person>>> lfResults = Futures.successfulAsList(lf1, lf2);
//assume lf1 failed
List<List<Person>> personLists = lfResults.get();
List<Person> listOne = personLists.get(0) //listOne is null
List<Person> listTwo = personLists.get(1) //listTwo, not null
結論
希望這有助于發現Google Guava的Futures類中包含的有用性。 我創建了一個單元測試,以顯示本文中描述的方法的示例用法。 由于有大量支持代碼,因此我在gihub上創建了一個項目guava-blog 。 該項目還將包含我以前在Guava上發表的文章( Monitor , ListenableFuture )的源代碼。 一如既往地歡迎提出意見和建議。
資源資源
- 番石榴項目首頁
- 期貨API
- 博客系列的源代碼
參考資料: Google Guava – JCG合作伙伴 Bill Bejeck的期貨,來自Random Thoughts On Coding博客。
翻譯自: https://www.javacodegeeks.com/2012/11/google-guava-futures.html