В чем преимущество передачи кода непосредственно в поток по сравнению с использованием CompletableFuture?
Thread thread = new Thread(() -> {do something});
thread.start();
ПРОТИВ
CompletableFuture<Void> cf1 =
CompletableFuture.runAsync(() -> {do something});




CompletableFuture.runAsync(...) запускает Runnable в forkJoin-Pool, который управляется, в то время как new Thread() создает новый поток, который ты должен управлять.
Что означает "управляется", он заранее выделен, а потоки совместно используются в JVM. Когда исполняемый файл завершен, поток можно повторно использовать для других исполняемых файлов. Это улучшает использование ресурсов, тем более что создание экземпляра потока - дорогостоящая операция - необходимо выделить не только объект, но и некоторую дополнительную память без кучи - стек потока.
Re: «... которым ты должен управлять». Нет. Вы не имеют, чтобы управлять этим. Вы можете создать новый поток, запустить его, а затем забыть о нем. Штраф Кроме за создание и уничтожение нового потока отсутствует.
@jameslarge Спасибо! Никогда не использовал потоки напрямую, всегда использовал Executors, поэтому не знал об этом.
@James, вы правы, «должен управлять» для любого значения слова «управлять», создание уже управляется, но вы также можете прервать его, дав ему имя или поместив его в группу потоков, и, конечно, вы могли бы все устаревшие и нежелательные вещи, такие как stop, suspend или `resume. Конечно, вы не можете имеют делать что-либо из этого (кроме создания), но вы могли бы, если вы увлечены низкоуровневой обработкой потоков (сосредоточьтесь на «как», а не на «что»). В случае CompletableFuture вы не можете этого сделать, потому что все сделано за вас, и вы работаете с высокоуровневым API для параллелизма (сосредоточьтесь на «что», а не «как»).
@jameslarge «Нет никаких штрафов, кроме затрат на создание и уничтожение», вы говорите так, будто у всех есть неограниченное количество ядер, а выполнение потоков ничего не стоит. Ну, это стоит процессорного времени, что является одной из причин использования пулов.
Некоторые утверждения здесь кажутся немного преувеличенными (@Cargeh и james big). Потоки используются не только для использования доступных ресурсов обработки (ядер), но также для сокрытия задержек или выполнения неблокирующих операций - даже если (новые) потоки в основном ждут (например, для ввода-вывода). Нет смысла иметь пул с потоками n, которые все ждут чего-то, что должен сделать (несуществующий) поток n+1. Конечно, такие вещи часто скрывают слои абстракции и библиотеки. Но создать новый поток вручную может будет прекрасно - даже если вы «опытны».
@Gerald Mücke уже упоминал о важном различии:
CompletableFuture.runAsync(...) runs the Runnable in the forkJoin-Pool which is managed, while new Thread() creates a new thread which you have to manage.
CompletableFuture будет использовать потоки, управляемые ThreadPool (по умолчанию или настроенные).
Однако я думаю, что следует также учитывать следующие два момента.
CompletableFuture имеет так много методов легко понять для цепь различных асинхронных вычислений вместе, что значительно упрощает внедрение асинхронности, чем прямое использование Нить.
CompletableFuture[] futures = IntStream.rangeClosed(0, LEN).boxed()
.map(i -> CompletableFuture.supplyAsync(() -> runStage1(i), EXECUTOR_SERVICE))
.map(future -> future.thenCompose(i -> CompletableFuture.supplyAsync(() -> runStage2(i), EXECUTOR_SERVICE)))
.toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(futures).join();
Вы никогда не должны забывать обрабатывать исключения; с CompletableFuture вы можете напрямую обрабатывать их следующим образом:
completableFuture.handle((s, e) -> e.toString()).join()
или воспользуйтесь ими таким образом, чтобы прервать вычисление:
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
в то время как вы легко столкнетесь с некоторыми серьезными проблемами при использовании Нить.
CompletableFuture - это обещание, которое использует ForkJoinPool по умолчанию (пул потоков, размер которого равен количеству процессоров), если он не предоставлен другим ThreadPool. Пул потоков будет содержать n или более потоков Thread.
Что еще мне нужно, если я буду придерживаться подхода
thread.startс точки зрения управления?