No history yet

Основы supplyAsync

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

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

Здесь на помощь приходит асинхронное программирование. Оно позволяет «делегировать» долгие задачи помощникам, не останавливая основную работу. В Java одним из ключевых инструментов для этого является CompletableFuture. Метод supplyAsync — это наш способ отдать задачу в фоновый режим и при этом ожидать, что в итоге мы получим какой-то результат.

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

public class SupplyAsyncExample {

    public static void main(String[] args) throws Exception {
        // Запускаем долгую задачу в фоновом потоке
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println("Начинаем долгую операцию в потоке: " + Thread.currentThread().getName());
                TimeUnit.SECONDS.sleep(2); // Имитация долгой работы
                System.out.println("Долгая операция завершена.");
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Результат долгой операции";
        });

        // Основной поток не ждет и продолжает свою работу
        System.out.println("Основной поток продолжает выполняться: " + Thread.currentThread().getName());

        // Получаем результат, когда он будет готов. Метод .get() блокирует поток до завершения задачи.
        String result = future.get(); 
        System.out.println("Получен результат: " + result);
    }
}

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

Supplier против Runnable

Для асинхронных задач в Java есть два основных «контракта» — функциональных интерфейса: Runnable и Supplier. Понимание их разницы — ключ к правильному использованию CompletableFuture.

Runnable представляет собой задачу, которая просто выполняется и ничего не возвращает. Это как команда «сделай что-то». Вы ее отдали и не ждете отчета. Например, «запиши лог в файл».

С другой стороны, — это задача, которая после выполнения поставляет результат. Это команда «добудь что-то». Вы ожидаете, что задача вернет вам значение, например, данные пользователя из базы или результат сложных вычислений. Именно поэтому метод supplyAsync требует Supplier — его цель «поставить» результат асинхронно.

ХарактеристикаRunnableSupplier
НазначениеВыполнить действиеВыполнить действие и вернуть результат
Сигнатура методаvoid run()T get()
Возвращает значение?НетДа
Пример использованияЛоггирование, отправка уведомленийЗапрос данных из сети, вычисления
Метод CompletableFuturerunAsyncsupplyAsync

Под капотом: ForkJoinPool

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

Использование общего пула избавляет нас от необходимости вручную создавать и настраивать потоки для каждой асинхронной операции. Java берет это на себя, эффективно распределяя нагрузку. Это похоже на то, как операционная система управляет процессами: вам не нужно думать, на каком ядре процессора запустить калькулятор, система делает это сама.

Хотя supplyAsync позволяет передать и собственный Executor (пул потоков), для большинства общих задач отлично подходит ForkJoinPool.commonPool() по умолчанию.

Итак, supplyAsync — это мощный и простой способ запустить задачу в фоновом режиме и получить от нее результат. Он инкапсулирует логику выполнения в Supplier и использует общий пул потоков для эффективной работы, оставляя основной поток вашего приложения свободным и отзывчивым.

Quiz Questions 1/5

В контексте аналогии с поваром на кухне, что представляет собой асинхронное программирование с использованием CompletableFuture?

Quiz Questions 2/5

Какой функциональный интерфейс следует передать в метод supplyAsync, если асинхронная задача должна вернуть результат (например, данные из базы)?