Используйте CompletableFuture на ForkJoinpool и избегайте ожидания потока
Здравствуйте, я думал с CompletableFuture и по умолчанию ForkJoinPool
Я мог бы оптимизировать выполнение задачи больше, чем классический ExecutorService
но я что-то упустил
С этим кодом выполнение занимает 1 секунду, у меня есть 3 рабочих потока:
for (int i = 0; i < 3; i++) {
final int counter = i;
listTasks.add(CompletableFuture.supplyAsync(() -> {
Thread.sleep(1000);
System.out.println("Looking up " + counter + " on thread " + Thread.currentThread().getName());
return null;
}));
}
ОК, кажется нормальным.
Но с этим кодом это займет 3 секунды:
for (int i = 0; i < 9; i++) {
final int counter = i;
listTasks.add(CompletableFuture.supplyAsync(() -> {
Thread.sleep(1000);
System.out.println("Looking up " + counter + " on thread " + Thread.currentThread().getName());
return null;
}));
}
Я думал, что спящий поток будет использоваться для запуска другого ожидающего задания, это должно занять и 1 секунду. Я читал, например, что состояние потока IO WAINTING будет означать, что поток может быть повторно использован для другой задачи. Могу ли я проверить это поведение с Thread.sleep()
? Мой метод тестирования неправильный или я что-то неправильно понял?
2 ответа
Спящий поток не может использоваться для выполнения работы другого потока (особенно, один поток не может спать для другого потока). Только центральный процессор может переключаться на второй поток, когда первый переходит в спящий режим.
Когда вы предоставляете задачу CompletableFuture.supplyAsync()
, это идет к экземпляру по умолчанию ForkJoinPool
у него столько потоков, сколько у вашего компьютера процессоров. Вы вручную устанавливаете выделенные три потока по умолчанию ForkJoinPool
Таким образом, ваши девять задач распределяются поровну между ними: каждый поток выполняет три задачи последовательно. Итак, у вас есть три секунды в результате.
Давайте сделаем математику. Если у вас есть 9 заданий, и каждое задание спит в течение 1 секунды, и у вас есть 2 процессора, вы можете запускать только 2 односекундных сна одновременно. Запустите это с 9 задачами, и вы получите по крайней мере 3 секунды или максимум 4 секунды.
Это не то же самое, что неблокирующий ввод-вывод, потому что этот поток Runnable связан с процессором и не будет отказывать ЦП до его завершения (несмотря на спящий режим). Если вы посмотрите на такие вещи, как пул потоков ввода-вывода RxJava, то для каждой задачи создается один поток, который приемлем для задач ввода-вывода (но не для задач, связанных с процессором).