Я работаю с CompletableFuture для асинхронного выполнения потока, созданного из источника списка.
поэтому я тестирую перегруженный метод, то есть SupplyAsync CompletableFuture, в котором один метод принимает только один параметр поставщика, а другой принимает параметр поставщика и параметр исполнителя. Вот документация для обоих:
один
SupplyAsync (поставщик-поставщик)
Возвращает новый CompletableFuture, который асинхронно завершается задачей, запущенной в ForkJoinPool.commonPool(), со значением, полученным путем вызова данного Supplier.
второй
SupplyAsync (поставщик-поставщик, исполнитель-исполнитель)
Возвращает новый CompletableFuture, который асинхронно завершается задачей, запущенной в данном исполнителе, со значением, полученным путем вызова данного Supplier.
И вот мой тестовый класс:
public class TestCompleteableAndParallelStream {
public static void main(String[] args) {
List<MyTask> tasks = IntStream.range(0, 10)
.mapToObj(i -> new MyTask(1))
.collect(Collectors.toList());
useCompletableFuture(tasks);
useCompletableFutureWithExecutor(tasks);
}
public static void useCompletableFutureWithExecutor(List<MyTask> tasks) {
long start = System.nanoTime();
ExecutorService executor = Executors.newFixedThreadPool(Math.min(tasks.size(), 10));
List<CompletableFuture<Integer>> futures =
tasks.stream()
.map(t -> CompletableFuture.supplyAsync(() -> t.calculate(), executor))
.collect(Collectors.toList());
List<Integer> result =
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
System.out.println(result);
executor.shutdown();
}
public static void useCompletableFuture(List<MyTask> tasks) {
long start = System.nanoTime();
List<CompletableFuture<Integer>> futures =
tasks.stream()
.map(t -> CompletableFuture.supplyAsync(() -> t.calculate()))
.collect(Collectors.toList());
List<Integer> result =
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
System.out.println(result);
}
}
class MyTask {
private final int duration;
public MyTask(int duration) {
this.duration = duration;
}
public int calculate() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(duration * 1000);
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
return duration;
}
}
в то время как метод useCompletableFuture занимает около 4 секунд, метод useCompletableFutureWithExecutor занимает всего 1 секунду.
Нет, мой вопрос: какую другую обработку выполняет ForkJoinPool.commonPool(), которая может выполнять накладные расходы? Разве мы не должны всегда отдавать предпочтение пользовательскому пулу исполнителей, а не ForkJoinPool?