No history yet

Основы CompletableFuture

Задачи, которые не заставляют ждать

Представьте, что вы заказываете кофе. Бариста принимает ваш заказ, затем уходит молоть зерна, варить эспрессо и взбивать молоко. Пока он занят вашим напитком, вся очередь стоит и ждет. Никто другой не может сделать заказ. Не очень эффективно, правда?

В программировании такой подход называется синхронным выполнением. Код выполняется строка за строкой, и если одна операция занимает много времени (например, загрузка файла из интернета), вся программа «зависает». Пользовательский интерфейс не отвечает, и приложение кажется сломанным.

Чтобы решить эту проблему, в Java существует механизм асинхронного программирования. Он позволяет запускать долгие задачи в фоновом режиме, не блокируя основной поток выполнения. Одним из самых удобных инструментов для этого является CompletableFuture.

Начало работы с supplyAsync

Метод supplyAsync — это наша отправная точка. Его название говорит само за себя: «поставить» (supply) результат «асинхронно» (async). Мы передаем ему задачу, и он немедленно возвращает нам специальный объект-контейнер — CompletableFuture. Этот объект — своего рода обещание, что результат задачи будет доступен в будущем.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class AsyncExample {
    public static void main(String[] args) {
        // Запускаем долгую задачу в фоновом режиме
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            // Имитируем долгую операцию, например, запрос к сети
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Данные успешно загружены"; // Этот результат будет в 'future'
        });

        System.out.println("Основная программа продолжает работать...");

        // Делаем что-то еще, пока задача выполняется
        System.out.println("Можно выполнять другие полезные действия.");

        // Чтобы увидеть результат, дожидаемся завершения
        String result = future.join(); // Этот вызов заблокирует поток до получения результата
        System.out.println("Полученный результат: " + result);
    }
}

В этом примере supplyAsync принимает лямбда-выражение, которое выполняется в отдельном потоке. Основной поток не ждет две секунды, а сразу же печатает сообщение «Основная программа продолжает работать...». CompletableFuture работает как контейнер, который в конечном итоге будет содержать либо результат вычислений («Данные успешно загружены»), либо ошибку, если что-то пошло не так.

Кто выполняет работу?

Когда мы вызываем supplyAsync, где именно выполняется наша задача? Она передается специальному исполнителю — общему пулу потоков . Представьте его как команду универсальных работников в офисе. Когда появляется новая задача, свободный работник берет ее и выполняет. Это избавляет нас от необходимости вручную создавать и управлять потоками, что является сложной и подверженной ошибкам задачей.

По умолчанию используется ForkJoinPool.commonPool(). Этот пул потоков оптимизирован для коротких вычислительных задач. Он эффективно распределяет нагрузку между доступными процессорными ядрами.

Использование supplyAsync с общим пулом позволяет вашему приложению оставаться отзывчивым, даже когда оно выполняет ресурсоемкие операции.

Теперь давайте проверим, как вы усвоили материал.

Quiz Questions 1/5

В аналогии с кофейней, какой процесс лучше всего описывает синхронное выполнение программы?

Quiz Questions 2/5

Что происходит сразу после вызова метода supplyAsync в основном потоке программы?

Итак, CompletableFuture и supplyAsync — это мощный старт в мир асинхронного программирования в Java. Они позволяют выполнять долгие операции, не жертвуя отзывчивостью приложения.