← Назад к вопросам

В чем разница между Future и CompletableFuture?

2.3 Middle🔥 121 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Future и CompletableFuture

Future (Java 5+)

Future — это интерфейс для представления результата асинхронного вычисления.

ExecutorService executor = Executors.newFixedThreadPool(2);

// Отправляем задачу в фон
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

// Ждём результат (блокирующий вызов)
String result = future.get();  // блокирует текущий поток
System.out.println(result);    // "Result"

// Проверяем статус
if (future.isDone()) {
    System.out.println("Готово");
}

// Отмена
future.cancel(true);  // попытка отменить задачу

Основные методы Future:

  • get() — получить результат (блокирует)
  • get(timeout, unit) — получить с таймаутом
  • isDone() — проверить завершение
  • isCancelled() — проверить отмену
  • cancel(mayInterrupt) — отменить задачу

Проблемы Future

Проблема 1: Блокирующий вызов

// Ждём результат — текущий поток заморожен
String result1 = future1.get();  // блокирует
String result2 = future2.get();  // блокирует
System.out.println(result1 + result2);

Это неэффективно в асинхронных системах. Поток впустую ждёт вместо выполнения других задач.

Проблема 2: Нельзя объединить несколько Future

// Хотим выполнить две задачи последовательно
Future<Integer> future1 = executor.submit(() -> 10);
Future<Integer> future2 = executor.submit(() -> 20);

// Как сложить? Нужно блокировать
int sum = future1.get() + future2.get();  // два блока

Проблема 3: Нельзя применить трансформацию

// Нет встроенного способа преобразовать результат
Future<String> future = executor.submit(() -> "5");
Future<Integer> intFuture = ???  // как конвертировать в int?

CompletableFuture (Java 8+)

CompletableFuture решает все проблемы Future. Это полнофункциональная реализация, которая позволяет:

// 1. Явно завершить результат (не нужно executor)
CompletableFuture<String> future = new CompletableFuture<>();
future.complete("Success");

String result = future.get();  // "Success"

// 2. Цепочка обработки (не блокирует)
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = f1.thenApply(x -> x * 2);  // 20
CompletableFuture<String> f3 = f2.thenApply(String::valueOf);  // "20"

// 3. Комбинирование Future
CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> 20);

CompletableFuture<Integer> combined = a.thenCombine(b, Integer::sum);
combined.whenComplete((result, exception) -> {
    if (exception == null) {
        System.out.println(result);  // 30
    }
});

Ключевые методы CompletableFuture

Создание:

// Сразу с результатом
CompletableFuture<String> f1 = CompletableFuture.completedFuture("value");

// Асинхронно (в ForkJoinPool)
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "result");

// Асинхронно в своём executor
ExecutorService executor = Executors.newFixedThreadPool(2);
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "result", executor);

// Пустой future для явного завершения
CompletableFuture<String> f4 = new CompletableFuture<>();
f4.complete("done");

Трансформация результата:

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 5);

// thenApply: синхронная трансформация
future.thenApply(x -> x * 2)     // 10
      .thenApply(String::valueOf) // "10"
      .thenAccept(System.out::println);

// thenApplyAsync: асинхронная трансформация в отдельном потоке
future.thenApplyAsync(x -> {
    Thread.sleep(1000);
    return x * 3;
});

Комбинирование нескольких Future:

CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);

// thenCombine: оба результата
CompletableFuture<Integer> sum = f1.thenCombine(f2, Integer::sum);  // 30

// thenAcceptBoth: обработать оба, ничего не вернуть
f1.thenAcceptBoth(f2, (a, b) -> System.out.println(a + b));

// allOf: дождаться всех
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2);
all.whenComplete((v, ex) -> System.out.println("All done"));

// anyOf: первый завершившийся
CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2);
any.whenComplete((result, ex) -> System.out.println(result));

Обработка ошибок:

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("Error!");
    }
    return 42;
});

// exceptionally: восстановление
future.exceptionally(ex -> {
    System.out.println("Ошибка: " + ex.getMessage());
    return 0;  // значение по умолчанию
});

// whenComplete: обработка в обоих случаях
future.whenComplete((result, exception) -> {
    if (exception != null) {
        System.out.println("Ошибка: " + exception);
    } else {
        System.out.println("Успех: " + result);
    }
});

// handle: трансформация с обработкой ошибок
future.handle((result, exception) -> {
    if (exception != null) return 0;
    return result * 2;
});

Практический пример: цепочка операций

// Без CompletableFuture (блокирующий, неудобный код)
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> f1 = executor.submit(() -> fetchUser("john"));
String user = f1.get();  // блокирует
Future<Integer> f2 = executor.submit(() -> getScore(user));
Integer score = f2.get();  // блокирует
Future<String> f3 = executor.submit(() -> formatResult(score));
String result = f3.get();  // блокирует

// С CompletableFuture (неблокирующий, красивый код)
CompletableFuture.supplyAsync(this::fetchUser)
    .thenApplyAsync(this::getScore)
    .thenApplyAsync(this::formatResult)
    .thenAccept(System.out::println)
    .exceptionally(ex -> {
        System.err.println("Pipeline failed: " + ex.getMessage());
        return null;
    });

Сравнение Future vs CompletableFuture

ХарактеристикаFutureCompletableFuture
Блокирующий get()ДаДа (но не нужен)
ТрансформацияНетДа (thenApply)
ОбъединениеНетДа (thenCombine)
Обработка ошибокНетДа (exceptionally)
Явное завершениеНетДа (complete)
Неблокирующие callbacksНетДа (whenComplete)
Асинхронные операцииНетДа (Async методы)
СложностьПростойПолнофункциональный

Выводы

  1. Future — базовый интерфейс, требует блокирующего get()
  2. CompletableFuture — современный инструмент для асинхронного программирования
  3. Используйте CompletableFuture для:
    • Цепочек асинхронных операций
    • Комбинирования результатов
    • Обработки ошибок
    • Неблокирующего кода
  4. Future ещё используется в legacy коде и как возвращаемый тип в некоторых библиотеках
В чем разница между Future и CompletableFuture? | PrepBro