[英]How to reorder Stream of CompletableFutures?
我處理CompletableFutures的流。 這些需要不同的時間才能完成。 那些花費較長時間進行塊流處理而其他人可能已經完成的處理(我知道並行流)
因此,我想對流中的項目進行重新排序(例如,使用緩沖區),以使完成的期貨前進。
例如,如果一個getUser調用花費很長時間,則此代碼將阻止流處理
public static Boolean isValid(User user) { ... }
emails.stream()
// not using ::
// getUser() returns CompletableFuture<User>
.map( e -> getUser(e))
// this line blocks Stream processing
.filter( userF -> isValid( userF.get()) )
.map( f -> f.thenApply(User::getName))
我想要一些類似的東西
emails.stream()
.map( e -> getUser(e))
// this moves Futures into a bounded buffer
// and puts those finished first
// like CompletionService [1]
// and returns a Stream again
.collect(FutureReorderer.collector())
// this is not the same Stream but
// the one created by FutureReorderer.collector()
.filter( userF -> isValid( userF.get()) )
.map( f -> f.thenApply(User::getName))
[1]例如,CompletionService https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorCompletionService.html在調用take()時返回已完成的任務,否則阻止。 但是CompletionService不接受期貨,有人需要做cs.sumbit(()-> f.get())嗎?
我該怎么做?
[編輯]
擁有更多上下文肯定會幫助您量身定制答案-我感到問題在其他地方,可以通過更輕松的方式解決。
但是,如果您的問題是如何在開始時以某種方式保留已完成的期貨,則幾乎沒有選擇:
使用自定義Comparator
Stream
進行排序:
.sorted(Comparator.comparing(f -> !f.isDone()))
請記住, isDone
不僅會在將來成功完成時返回true。
將期貨存儲在PriorityQueue
PriorityQueue<CompletableFuture<String>> queue
= new PriorityQueue<>(Comparator.comparing(f -> !f.isDone()));
在輪詢元素時,隊列將根據其提供的順序返回元素。
它在起作用:
PriorityQueue<CompletableFuture<String>> queue
= new PriorityQueue<>(Comparator.comparing(f -> !f.isDone()));
queue.add(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) { }
return "42";
}));
queue.add(CompletableFuture.completedFuture("completed"));
queue.poll(); // "completed"
queue.poll(); // still going on
重要的是要記住,如果您確實想將PriorityQueue
轉換為Stream
,則不能僅使用stream()
來做到這一點-這不會保留優先級順序。
這是正確的方法:
Stream.generate(queue::poll).limit(queue.size())
我假設OP中的要求是同時執行getUser
並按完成順序處理結果Futures。 這是ExecutorCompletionService
解決方案:
final CompletionService<User> ecs = new ExecutorCompletionService<>(executor);
emails.stream().map(e -> ecs.submit(() -> getUser(e).get()))
.collect(Collectors.collectingAndThen(Collectors.toList(), fs -> fs.stream())) // collect the future list for concurrent execution
.map(f -> {
try {
return ecs.take().get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
})
.filter(u -> isValid(u)).map(User::getName)... //TODO;
要么:
final BlockingQueue<Future<User>> queue = new ArrayBlockingQueue<>(emails.size());
final CompletionService<User> ecs = new ExecutorCompletionService<>(executor, queue);
emails.stream().forEach(e -> ecs.submit(() -> getUser(e).get()));
IntStream.range(0, emails.size())
.mapToObj(i -> {
try {
return queue.poll().get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
})
.filter(u -> isValid(u)).map(User::getName);
這很簡單,但並不簡單。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.