the*_*nov 4 java executorservice completable-future
假设我们有 n 个独立的阻塞 IO 任务,例如对另一台服务器进行休息调用的任务。然后我们需要合并所有答案。每个任务的处理时间可以超过 10 秒。
我们可以按顺序处理它,最后花费 ~n*10 秒:
Task1Ans task1 = service1.doSomething();
Task2Ans task2 = service2.doSomething()
...
return result;
Run Code Online (Sandbox Code Playgroud)
另一种策略是使用 CompletableFuture 以并行方式处理它,并在所有任务上花费约 10 秒:
CompletableFuture<Task1Ans> task1Cs = CompletableFuture.supplyAsync(() -> service1.doSomething(), bestExecutor);
CompletableFuture<Task2Ans> task2Cs = CompletableFuture.supplyAsync(() -> service2.doSomething(), bestExecutor);
return CompletableFuture.allOf(task1Cs, task2Cs)
.thenApply(nothing -> {
...
// combine task1, task2 into result object
return result;
}).join();
Run Code Online (Sandbox Code Playgroud)
第二种方法有好处,但我不明白哪种类型的线程池最适合此类任务:
ExecutorService bestExecutor = Executors.newFixedThreadPool(30) /// or Executors.newCachedThreadPool() or Executors.newWorkStealingPool()
Run Code Online (Sandbox Code Playgroud)
我的问题是哪个 ExecutorService 最适合处理 n 并行阻塞 IO 任务。
在完全受 CPU 限制的任务中,使用比 CPU 核心更多的线程并不能获得额外的性能。所以在这种场景下,8核/8线程的CPU只需要8个线程就可以最大化性能,而过多则会损失性能。IO 任务通常通过使用比 CPU 核心更多的线程来提高性能,因为在等待 IO 时 CPU 时间可以用来做其他事情。但即使每个线程的 CPU 开销较低,扩展也会受到限制,因为每个线程都会占用内存,并导致缓存/上下文切换。
鉴于您的任务受到 IO 限制,并且您没有提供任何其他约束,您可能应该为每个 IO 任务运行不同的线程。您可以通过使用固定或缓存线程池来实现此目的。
如果您的 IO 任务数量非常大(数千+),您应该限制线程池的最大大小,因为您可能会有太多线程。
如果您的任务受 CPU 限制,您应该再次将线程池限制为更小的大小。可以使用以下方法动态获取核心数:
int cores = Runtime.getRuntime().availableProcessors();
Run Code Online (Sandbox Code Playgroud)
此外,正如您的 CPU 有缩放限制一样,您的 IO 设备通常也有缩放限制。您不应该超过该限制,但如果不进行测量,就很难说出限制在哪里。
| 归档时间: |
|
| 查看次数: |
2016 次 |
| 最近记录: |