Как использовать 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, предположительно, этот метод не блокируется и не должен выполняться асинхронно.