Java 如何有效地使用CompletableFuture映射每个输入的异步任务
我想返回由所有键到值的映射组成的映射,即对这些键的API响应。为此,我使用了Java 如何有效地使用CompletableFuture映射每个输入的异步任务,java,multithreading,asynchronous,guava,completable-future,Java,Multithreading,Asynchronous,Guava,Completable Future,我想返回由所有键到值的映射组成的映射,即对这些键的API响应。为此,我使用了CompletableFuture和Guava。下面是我的尝试。使用Java8和线程API是否还有其他标准方法可以实现同样的效果 映射为id->apiResponse(id) 公共静态列表returnAPIResponse(整数键){ return list.newArrayList(key.toString()+“Test”); } 公共静态void main(字符串[]args){ List key=Lists.n
CompletableFuture
和Guava
。下面是我的尝试。使用Java8和线程API是否还有其他标准方法可以实现同样的效果
映射为id->apiResponse(id)
公共静态列表returnAPIResponse(整数键){
return list.newArrayList(key.toString()+“Test”);
}
公共静态void main(字符串[]args){
List key=Lists.newArrayList(1,2,3,4);
列表未来=键
.stream()
.map(键->CompletableFuture.SupplySync(
()->新的AbstractMap.SimpleEntry(键,returnAPIResponse(键)))
.collect(Collectors.toList());
System.out.println(
futures.parallelStream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
这里有一个有趣的行为,我将尽力解释。让我们从简单开始,让我们暂时忘记CompletableFuture
,只需使用简单的parallelStream
,并添加一个小的调试步骤:
List<Integer> keys = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
Map<Integer, List<String>> result =
keys.parallelStream()
.map(x -> new AbstractMap.SimpleEntry<>(x, returnAPIResponse(x)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println("parallelism : " + pool.getParallelism() + " current : " + pool.getPoolSize());
我假设您已经知道,parallelStream
的操作是在common
ForkJoinPool
中执行的。这个输出的含义可能也很明显:11个线程可用,并且所有线程都被使用了
现在我将稍微修改您的示例:
List<Integer> keys = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
ForkJoinPool pool = ForkJoinPool.commonPool();
ExecutorService supplyPool = Executors.newFixedThreadPool(2);
Map<Integer, List<String>> result =
keys.parallelStream()
.map(x -> CompletableFuture.supplyAsync(
() -> new AbstractMap.SimpleEntry<>(x, returnAPIResponse(x)),
supplyPool
))
.map(CompletableFuture::join)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println("parallelism : " + pool.getParallelism() + " current : " + pool.getPoolSize());
惊喜。创建了更多的线程,然后我们想要什么?那么,getPoolSize
的文档说明:
返回已启动但尚未终止的工作线程数。此方法返回的结果可能与getParallelism不同,getParallelism创建线程是为了在其他线程被合作阻止时保持并行性
本例中的阻塞是通过map(CompletableFuture::join)
实现的。您已经有效地阻止了来自ForkJoinPool
的工作线程,它通过旋转另一个工作线程来补偿这一点
如果您不想陷入这样的意外:
List<CompletableFuture<AbstractMap.SimpleEntry<Integer, List<String>>>> list =
keys.stream()
.map(x -> CompletableFuture.supplyAsync(
() -> new AbstractMap.SimpleEntry<>(x, returnAPIResponse(x)),
supplyPool
))
.collect(Collectors.toList());
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();
Map<Integer, List<String>> result =
list.stream()
.map(CompletableFuture::join)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
但不会生成补偿线程。由于CompletableFuture.allOf
返回一个CompletableFuture
,我需要再次流式处理以获得结果
不要让上一个流操作中的.map(CompletableFuture::join)
愚弄你,因为上一个CompletableFuture::allOf
已经被阻塞,等待所有任务完成。hmm,你的问题是什么?如果您想要一个Map
,只需替换collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue))
我的问题是,如果我提供的代码是这样做的吗?最后,我希望为异步传递到映射中的每个数据键获取API响应。映射为id->apiResponse(id)。你仍然需要最终阻止某个地方以获得结果,就像你对连接
所做的那样,你明白这一点,对吧?是的,这一部分是可以的,但是CompletableFuture.SupplySync的实际使用和构造我不太担心。这段代码可以完成这项工作,但我想知道是否有其他更好的方法来完成这项工作。谢谢你,这是一篇非常有用的文章!对这个话题有了一些见解。
List<Integer> keys = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
ForkJoinPool pool = ForkJoinPool.commonPool();
ExecutorService supplyPool = Executors.newFixedThreadPool(2);
Map<Integer, List<String>> result =
keys.parallelStream()
.map(x -> CompletableFuture.supplyAsync(
() -> new AbstractMap.SimpleEntry<>(x, returnAPIResponse(x)),
supplyPool
))
.map(CompletableFuture::join)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println("parallelism : " + pool.getParallelism() + " current : " + pool.getPoolSize());
parallelism : 11 current : 16
List<CompletableFuture<AbstractMap.SimpleEntry<Integer, List<String>>>> list =
keys.stream()
.map(x -> CompletableFuture.supplyAsync(
() -> new AbstractMap.SimpleEntry<>(x, returnAPIResponse(x)),
supplyPool
))
.collect(Collectors.toList());
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();
Map<Integer, List<String>> result =
list.stream()
.map(CompletableFuture::join)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();