【问题标题】:For loop wait for all CompletableFutures to give result, and add result to intFor循环等待所有CompletableFutures给出结果,并将结果添加到int
【发布时间】:2020-09-13 14:08:16
【问题描述】:

我有一个方法 getCount(id) 从数据库中返回一个整数,作为 CompletableFuture。我想遍历所有的 id,得到所有的结果,并将它们添加到一个全局 int 中。然后我想用那个 int 做点什么:

int totalCount;
for (String id : API.getIDs()) {
    //OtherAPI.getCount(id) will return the CompletableFuture<Integer>
    OtherAPI.getCount(id).thenAccept(count -> totalCount += count);
}

System.out.println("" + totalCount);

这会报错,因为你不能在 thenAccept 中将一个整数加到另一个整数上,因为它是一个消费者。这样做的方法是什么?

【问题讨论】:

  • “行不通”是什么意思?编译器错误?运行时异常?意外行为?请edit发帖并提供必要的信息。
  • 已添加,但我添加了更多“将不起作用”以明确表示无法以这种方式完成。
  • 您不能更改 lambda 中的外部范围变量,因为该变量必须是最终的或有效最终的 wrt。拉姆达。您可以使用AtomicInteger 来避免此问题。
  • 是的,我知道,但我不知道该怎么做。这就是这个问题的原因。
  • 有一个解决方案给你stackoverflow.com/questions/30026824/…

标签: java for-loop completable-future


【解决方案1】:

请注意,传递给thenAccept 的函数可以由任意线程执行,所以谢天谢地,Java 不允许他们修改局部变量。当您将变量更改为堆上的字段时,编译器会接受它,但结果会完全破坏。

最简单的解决方案是

int totalCount = 0;
for(String id: API.getIDs()) {
    totalCount += OtherAPI.getCount(id).join();
}

它只是等待每个值的可用性,然后在本地对它们求和。根据OtherAPI.getCount(…) 调用所封装的操作,这甚至可能是最有效的解决方案。

但是当这些操作花费大量时间并且可以真正并行运行时,即不依赖于内部共享资源时,在所有操作开始之前不要等待可能是有益的。

你可以这样做

CompletableFuture<Integer> result = CompletableFuture.completedFuture(0);
for(String id: API.getIDs()) {
    result = result.thenCombine(OtherAPI.getCount(id), Integer::sum);
}
System.out.println(result.join());

在这里,循环完全无需等待。它将安排求和操作,在两个操作完成后完成。然后,只有最后的join 调用会等待最终结果。至此,所有异步操作都已经提交完毕。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 2019-04-08
    相关资源
    最近更新 更多