![](/img/trans.png)
[英]Java - Mockito - CompletableFutures - Unexpected Result of Tested Method
[英]get method on Future of CompletableFutures
我在 java 中做一些多線程工作,我對 Future get
方法有疑問。
我有一個提交 n Callable 的ExecutorSevice
。 提交返回一個Future
,它被添加到一個列表中(稍后在一些代碼中)。 Callable 的call
方法遍歷一個列表,並且對於列表中的每一項, someMethod
都會被調用並返回一個CompletableFuture<T>
。 然后,Completable future 被添加到 map。
在循環call
結束時返回包含一堆 CompletableFuture 的 map。
因此,初始列表 ( uglyList
) 包含<String, CompletableFuture<String>>
的 n 個映射。
我的問題是,當我在uglyList
列表的元素上調用get
時,執行被阻止,直到訪問整個列表(作為參數傳遞給 Callable)並且所有CompletableFutures
都已插入 map 中,對嗎?
因為,我有一個疑問,這個例子中的get
是否有可能也等待 map 中的CompletableFutures
完成? 我不知道如何測試這個
public class A {
private class CallableC implements Callable<Map<String, CompletableFuture<String>>> {
private List<String> listString;
Map<String, CompletableFuture<String>> futureMap;
public CallableC(List<String> listString) throws Exception {
this.listString = listString;
this.futureMap = new HashMap<>();
}
@Override
public Map<String, CompletableFuture<String>> call() throws Exception {
for (int j = 0; j < listString.size(); ++j) {
CompletableFuture<String> future = someMethod();
futureMap.put(listString.get(i), future);
}
return futureMap;
}
}
public void multiThreadMethod(List<String> items) throws Exception {
List<Future<Map<String, CompletableFuture<String>>>> uglyList = new ArrayList<>();
int coresNumber = (Runtime.getRuntime().availableProcessors());
// This method split a List in n subList
List<List<String>> splittedList = split(items, coresNumber);
ExecutorService executor = Executors.newFixedThreadPool(coresNumber);
try {
for (int i = 0; i < coresNumber; ++i) {
threads.add(executor.submit(new CallableC(splittedList.get(i),)));
}
for (int i = 0; i < coresNumber; ++i) {
uglyList.get(i).get(); //here
}
} catch (Exception e) {
// YAY errors
}
finally {
executor.shutdown();
}
}
}
當我在丑陋列表的一個元素上調用 get 時,執行被阻止,直到訪問整個列表(作為參數傳遞給 Callable)並且所有 CompletableFutures 都已插入 map,對嗎?
對,那是正確的。 get()
將等待Callable
的完成,在您的情況下,這意味着CallableC#call()
方法的結束,該方法將在 for 循環結束時到達,此時CompletableFutures
全部創建並插入futureMap
,無論這些CompletableFuture
的狀態/完成情況如何。
有可能這個例子中的 get 還等待 map 中的 CompletableFutures 完成?
有可能實現這一點,但並非沒有附加代碼。
在您的代碼中執行此操作的最小方法是在任務內的CompletableFuture
上添加一個加入調用。
@Override
public Map<String, CompletableFuture<String>> call() throws Exception {
for (int j = 0; j < listString.size(); ++j) {
CompletableFuture<String> future = someMethod();
futureMap.put(listString.get(j), future);
}
// This will wait for all the futures inside the map to terminate
try {
CompletableFuture.allOf(futureMap.values().toArray(new CompletableFuture[0])).join();
} catch (CompletionException e) {
// ignored - the status will be checked later on
}
return futureMap;
}
另一種方法是在最后等待(為簡潔起見,我跳過異常處理):
for (int i = 0; i < coresNumber; ++i) {
threads.add(executor.submit(new CallableC(splittedList.get(i),)));
}
// This will wait for all the futures to terminate (thanks to f.get())
// And then, all the completable futures will be added to an array of futures that will also be waited upon
CompletableFuture.allOf(
uglyList.stream().flatMap(f -> f.get().values().stream()).toArray(CompletableFuture[]::new)
).join();
但是,如果可能的話,我會通過僅使用可完成的期貨來簡化整個代碼,除非您有充分的理由以您的方式中斷任務(使用列表列表、專用執行程序等)。 這可能看起來像這樣(可能有一些拼寫錯誤)
List<String> items = ...
ConcurrentMap<String, String> resultsByItem = new ConcurrentHashMap<>();
Future<Void> allWork = CompletableFuture.allOf(
items.stream()
.map(item -> someWork(item) // Get a completable future for this item
.andThen(result -> resultsByItem.put(item, result)) // Append the result to the map
).toArray(CompletableFuture[]::new)
);
allWork.join();
// Your futureMap is completed.
這將替換問題中的整個代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.