Тема против CompletableFuture

В чем преимущество передачи кода непосредственно в поток по сравнению с использованием CompletableFuture?

Thread thread = new Thread(() -> {do something});
thread.start();

ПРОТИВ

CompletableFuture<Void> cf1 =  
     CompletableFuture.runAsync(() -> {do something}); 
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
27
0
7 154
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

CompletableFuture.runAsync(...) запускает Runnable в forkJoin-Pool, который управляется, в то время как new Thread() создает новый поток, который ты должен управлять.

Что означает "управляется", он заранее выделен, а потоки совместно используются в JVM. Когда исполняемый файл завершен, поток можно повторно использовать для других исполняемых файлов. Это улучшает использование ресурсов, тем более что создание экземпляра потока - дорогостоящая операция - необходимо выделить не только объект, но и некоторую дополнительную память без кучи - стек потока.

Что еще мне нужно, если я буду придерживаться подхода thread.start с точки зрения управления?

Sagar 16.04.2018 23:23

Re: «... которым ты должен управлять». Нет. Вы не имеют, чтобы управлять этим. Вы можете создать новый поток, запустить его, а затем забыть о нем. Штраф Кроме за создание и уничтожение нового потока отсутствует.

Solomon Slow 16.04.2018 23:44

@jameslarge Спасибо! Никогда не использовал потоки напрямую, всегда использовал Executors, поэтому не знал об этом.

Sagar 16.04.2018 23:54

@James, вы правы, «должен управлять» для любого значения слова «управлять», создание уже управляется, но вы также можете прервать его, дав ему имя или поместив его в группу потоков, и, конечно, вы могли бы все устаревшие и нежелательные вещи, такие как stop, suspend или `resume. Конечно, вы не можете имеют делать что-либо из этого (кроме создания), но вы могли бы, если вы увлечены низкоуровневой обработкой потоков (сосредоточьтесь на «как», а не на «что»). В случае CompletableFuture вы не можете этого сделать, потому что все сделано за вас, и вы работаете с высокоуровневым API для параллелизма (сосредоточьтесь на «что», а не «как»).

Gerald Mücke 17.04.2018 09:07

@jameslarge «Нет никаких штрафов, кроме затрат на создание и уничтожение», вы говорите так, будто у всех есть неограниченное количество ядер, а выполнение потоков ничего не стоит. Ну, это стоит процессорного времени, что является одной из причин использования пулов.

Cargeh 17.04.2018 09:14

Некоторые утверждения здесь кажутся немного преувеличенными (@Cargeh и james big). Потоки используются не только для использования доступных ресурсов обработки (ядер), но также для сокрытия задержек или выполнения неблокирующих операций - даже если (новые) потоки в основном ждут (например, для ввода-вывода). Нет смысла иметь пул с потоками n, которые все ждут чего-то, что должен сделать (несуществующий) поток n+1. Конечно, такие вещи часто скрывают слои абстракции и библиотеки. Но создать новый поток вручную может будет прекрасно - даже если вы «опытны».

Marco13 17.04.2018 13:09

@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.

Другие вопросы по теме