我正在学习并尝试将CompletableFuture应用于我的问题陈述。我有一个我正在迭代的项目列表。
Prop是一个只有两个属性prop1和prop2的类,分别是getter和setter。
List<Prop> result = new ArrayList<>();
for ( Item item : items ) {
item.load();
Prop temp = new Prop();
// once the item is loaded, get its properties
temp.setProp1(item.getProp1());
temp.setProp2(item.getProp2());
result.add(temp);
}
return result;
但是,item.load()这里是一个阻塞调用。所以,我正在考虑使用下面的CompletableFuture -
for (Item item : items) {
CompletableFuture<Prop> prop = CompletableFuture.supplyAsync(() -> {
try {
item.load();
return item;
} catch (Exception e) {
logger.error("Error");
return null;
}
}).thenApply(item1 -> {
try {
Prop temp = new Prop();
// once the item is loaded, get its properties
temp.setProp1(item.getProp1());
temp.setProp2(item.getProp2());
return temp;
} catch (Exception e) {
}
});
}
但我不知道如何等待加载所有项目然后汇总并返回结果。
我实施CompletableFutures的方式可能完全错误,因为这是我的第一次尝试。请原谅任何错误。提前感谢您的帮助。
答案 0 :(得分:3)
您使用CompletableFuture
的方法存在两个问题。
首先,你说item.load()
是一个阻塞调用,因此CompletableFuture
的默认执行程序不适合它,因为它试图达到与CPU核心数相匹配的并行度。您可以通过将不同的Executor
传递给CompletableFuture
的异步方法来解决此问题,但您的load()
方法不会返回后续操作所依赖的值。因此CompletableFuture
的使用使设计复杂化而没有任何好处。
您可以异步执行load()
调用,只需使用ExecutorService
等待完成,然后按原样循环(当然没有执行load()
操作) :
ExecutorService es = Executors.newCachedThreadPool();
es.invokeAll(items.stream()
.map(i -> Executors.callable(i::load))
.collect(Collectors.toList()));
es.shutdown();
List<Prop> result = new ArrayList<>();
for(Item item : items) {
Prop temp = new Prop();
// once the item is loaded, get its properties
temp.setProp1(item.getProp1());
temp.setProp2(item.getProp2());
result.add(temp);
}
return result;
您可以通过选择执行程序来控制并行度,例如:你可以使用Executors.newFixedThreadPool(numberOfThreads)
代替无界线程池。