我想知道使用CompletableFuture的最佳实践。
如果我有一个整数列表,对于每个整数,我想执行3个动作,这需要一些时间。例如,相乘,相除和相减。
对于结果,它应该是map,其中key是整数,值是具有三个操作结果的列表。
方案1:如果我要在处理完一个整数后将结果写入映射,那么这低于最佳实践吗?
for (int x : nums) {
CompletableFuture<Integer> multiply = CompletableFuture.supplyAsync(() -> multiply(x));
CompletableFuture<Integer> divide = CompletableFuture.supplyAsync(() -> divide(x));
CompletableFuture<Integer> subtract = CompletableFuture.supplyAsync(() -> subtract(x));
CompletableFuture<Void> allRequests = CompletableFuture.allOf(multiple, divide, substract);
//iterate the three CompletableFuture and add to the map.
}
方案2:如果我想将结果写到地图上,直到完成所有过程。
List<CompletableFuture<Void>> allRequests = new ArrayList<>();
for (int x : nums) {
CompletableFuture<Integer> multiply = CompletableFuture.supplyAsync(() -> multiply(x));
CompletableFuture<Integer> divide = CompletableFuture.supplyAsync(() -> divide(x));
CompletableFuture<Integer> subtract = CompletableFuture.supplyAsync(() -> subtract(x));
CompletableFuture<Void> allRequestsPerNum = CompletableFuture.allOf(multiple, divide, substract);
allRequests.add(allRequestsPerNum);
}
CompletableFuture.allOf(allRequest);
//How am I suppose to build the result?
对于场景2,看起来如果我们在for循环内调用CompletableFuture.allOf
,这意味着在移至下一个数字之前需要完成三个操作?
您可以使用CompletableFuture
中内置的功能,未经测试,因此可能存在一些小问题。它应该给您一个不错的主意。
List<CompletableFuture<Void>> allRequests = new ArrayList<>();
for (int x : nums) {
allRequests.add(CompletableFuture.allOf(
CompletableFuture.supplyAsync(() -> multiply(x)).thenApplyAsync(() -> addToMap()),
CompletableFuture.supplyAsync(() -> divide(x)).thenApplyAsync(() -> addToMap()),
CompletableFuture.supplyAsync(() -> subtract(x)).thenApplyAsync(() -> addToMap())));
}
CompletableFuture.allOf(allRequests.toArray(new CompletableFuture[])).join();
类似以下内容应该适合您:
如评论中所述,我不保证通过这种方式进行操作会带来任何性能上的好处。我也没有保证这是这样做的“最佳实践”。
// Keep all the futures around
final Collection<CompletableFuture<?>> futures = new ArrayList<>();
// And prepare the resulting map
final Map<Integer, List<Integer>> map = new HashMap<>();
for (int x : Arrays.asList(1, 2, 3)) {
// Create the map value List.
// Initialise it with some default values so that 'set'
// works. Use a concurrent-safe List so that there's
// no risk of corruption from the async operations.
final List<Integer> value = new CopyOnWriteArrayList<>(Arrays.asList(0, 0, 0));
// Put the key and List (not yet ready) into the Map
map.put(x, value);
// Add all of your futures to your tracking Collection.
// Note: each future now includes setting the appropriate
// entry in the List to that result.
futures.add(CompletableFuture.supplyAsync(() -> 2 * x)
.thenAcceptAsync(result -> value.set(0, result)));
futures.add(CompletableFuture.supplyAsync(() -> 2 / x)
.thenAcceptAsync(result -> value.set(1, result)));
futures.add(CompletableFuture.supplyAsync(() -> 2 - x)
.thenAcceptAsync(result -> value.set(2, result)));
}
// Wait for them all to complete.
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();