Как использовать CompletableFuture.thenComposeAsync ()?
дано:
public class Test
{
public static void main(String[] args)
{
int nThreads = 1;
Executor e = Executors.newFixedThreadPool(nThreads);
CompletableFuture.runAsync(() ->
{
System.out.println("Task 1. Thread: " + Thread.currentThread().getId());
}, e).thenComposeAsync((Void unused) ->
{
return CompletableFuture.runAsync(() ->
{
System.out.println("Task 2. Thread: " + Thread.currentThread().getId());
}, e);
}, e).join();
System.out.println("finished");
}
}
Я ожидаю, что один поток исполнителя выполнит задачу 1, а затем задачу 2. Вместо этого код зависает, если nThreads
меньше 2.
- Пожалуйста, объясните, почему код зависает. Я вижу, что он заблокирован в CompletableFuture: 616 ждут подарки!--2--> для завершения, но непонятно почему.
- если я разрешаю использовать 2 потока, для чего используется каждый поток?
короче говоря, Пожалуйста, помогите мне понять как thenComposeAsync()
на самом деле работает. Javadoc выглядит так, как будто он был написан для роботов вместо людей:)
2 ответов
на
thenComposeAsync
метод помещает новую задачу для вашего исполнителя, который захватывает один поток и ждет вашегоTask 2
завершить. Но у этого больше нет ниток. Вместо этого вы можете использоватьthenCompose
метод, который выполняется в том же потоке, что иTask 1
чтобы избежать взаимоблокировок.выполняется один поток
Task 1
иTask 2
и второй заботится о составлении результатов двух.
Примечание.: CompletableFuture
(s) Лучше всего работать с ForkJoinPool
это более эффективно при обработке задач, которые порождают новые задачи. Значение по умолчанию ForkJoinPool
был добавлен в Java 8 для этой цели и используется по умолчанию, если не указать исполнитель для выполнения ваших задач.
вот хорошая презентация о том, где эти новые функции светит и как они работают:реактивные Шаблоны программирования С Java 8 Futures.
блокирует на runAsync
что внутри thenComposeAsync
. thenComposeAsync
запускает поставляемую функцию в потоке внутри executor e. Но функция, которую вы ей дали, пытается выполнить тело runAsync внутри того же исполнителя.
вы можете лучше видеть, что происходит, добавив другой вывод трассировки:
CompletableFuture.runAsync(() -> {
System.out.println("Task 1. Thread: " + Thread.currentThread().getId());
}, e).thenComposeAsync((Void unused) -> {
System.out.println("Task 1 1/2. Thread: " + Thread.currentThread().getId());
return CompletableFuture.runAsync(() -> {
System.out.println("Task 2. Thread: " + Thread.currentThread().getId());
}, e);
}, e).join();
теперь, если вы запустите его с помощью 2-thread executor, вы увидите, что задача 1 1/2 и Задача 2 выполняются в разных потоках.
способ исправить это-заменить thenComposeAsync
с обычными thenCompose
. Я не уверен, почему вы могли бы использовать thenComposeAsync
. Если у вас есть метод, который возвращает CompletableFuture
, предположительно, этот метод не блокируется и не должен выполняться асинхронно.