0Pricing
Java Academy · Урок

Future и обработка ошибок

Используйте Future.get с ограничениями времени, обрабатывайте ExecutionException и отменяйте выполняющиеся задачи.

«Future и обработка ошибок» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Что такое отложенный результат

Объект Future<V> представляет ожидаемый результат асинхронного вычисления. Он предоставляет методы для проверки завершения, ожидания результата и обработки ошибок.

import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
    Thread.sleep(1000);
    return 42;
});

System.out.println("Is done? " + future.isDone()); // false
Integer result = future.get(); // blocks
System.out.println("Result: " + result); // 42
pool.shutdown();

future.get() с ограничением времени

Всегда отдавайте предпочтение варианту с ограничением времени, чтобы предотвратить бесконечное зависание:

try {
    Integer result = future.get(5, TimeUnit.SECONDS);
    System.out.println(result);
} catch (TimeoutException e) {
    System.out.println("Timed out");
    future.cancel(true); // interrupt the task
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
} catch (ExecutionException e) {
    System.out.println("Task threw: " + e.getCause());
}

ExecutionException

Если задача выбросила исключение, future.get() оборачивает его в ExecutionException. Извлеките исходное исключение с помощью getCause():

Future<String> failing = pool.submit(() -> {
    throw new IllegalArgumentException("bad input");
});

try {
    failing.get();
} catch (ExecutionException e) {
    Throwable cause = e.getCause();
    System.out.println(cause.getClass().getSimpleName()); // IllegalArgumentException
    System.out.println(cause.getMessage()); // bad input
}

Отмена отложенного результата

cancel(mayInterruptIfRunning) пытается отменить задачу. Метод возвращает true при успешной отмене. Если mayInterruptIfRunning равно true, выполняющемуся потоку отправляется прерывание.

Future<String> f = pool.submit(() -> {
    try {
        Thread.sleep(10_000); // long task
        return "done";
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return "interrupted";
    }
});

boolean cancelled = f.cancel(true); // send interrupt
System.out.println("Cancelled: " + cancelled); // true
System.out.println("Is cancelled: " + f.isCancelled()); // true

isCancelled и isDone

isDone() возвращает true, если задача завершилась обычным образом, выбросила исключение или была отменена. isCancelled() возвращает true только в случае отмены задачи.

// After cancel:
System.out.println(f.isDone());       // true
System.out.println(f.isCancelled()); // true

// After normal completion:
Future<Integer> done = pool.submit(() -> 5);
done.get(); // wait
System.out.println(done.isDone());       // true
System.out.println(done.isCancelled()); // false

CompletionService для получения первых готовых результатов

ExecutorCompletionService оборачивает пул и предоставляет метод take() для получения следующего завершённого Future (в порядке завершения, а не отправки):

ExecutorCompletionService<Integer> cs =
    new ExecutorCompletionService<>(pool);

for (int i = 0; i < 5; i++) {
    final int delay = 5 - i; // submit fastest last
    cs.submit(() -> { Thread.sleep(delay * 100); return delay; });
}

for (int i = 0; i < 5; i++) {
    Future<Integer> f = cs.take(); // next completed
    System.out.println(f.get());
}

Безопасная обработка нескольких отложенных результатов

При сборе результатов множества отложенных результатов перебирайте их и обрабатывайте исключения для каждого результата отдельно:

List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    final int id = i;
    futures.add(pool.submit(() ->
        id % 2 == 0 ? "ok_" + id : (() -> { throw new RuntimeException("fail_"+id); }).get()
    ));
}

for (Future<String> f : futures) {
    try {
        System.out.println(f.get());
    } catch (ExecutionException e) {
        System.out.println("Error: " + e.getCause().getMessage());
    }
}

Future.get() после завершения работы пула

Отправка задачи после shutdown() вызывает RejectedExecutionException. Однако результаты задач, отправленных ДО завершения работы, всё ещё можно получить после него:

ExecutorService pool2 = Executors.newFixedThreadPool(2);
Future<Integer> f = pool2.submit(() -> 99);
pool2.shutdown(); // no new tasks

// Still valid — task was submitted before shutdown:
System.out.println(f.get()); // 99

try {
    pool2.submit(() -> 0); // throws!
} catch (RejectedExecutionException e) {
    System.out.println("Pool shut down");
}

FutureTask: ручной отложенный результат

FutureTask<V> реализует одновременно Runnable и Future<V> — это удобно, когда нужен Future без ExecutorService:

FutureTask<String> task = new FutureTask<>(() -> "computed");
new Thread(task).start(); // run in any thread
System.out.println(task.get()); // computed

Шаблон повторных попыток с отложенным результатом

Реализуйте простую логику повторных попыток вокруг future.get():

Future<String> future = pool.submit(() -> {
    if (Math.random() < 0.7) throw new RuntimeException("transient error");
    return "success";
});

for (int attempt = 0; attempt < 3; attempt++) {
    try {
        System.out.println(future.get());
        break;
    } catch (ExecutionException e) {
        System.out.println("Attempt " + (attempt+1) + " failed");
        if (attempt == 2) throw e;
        future = pool.submit(() -> "retry"); // resubmit
    }
}

Ограничения отложенного результата

У java.util.concurrent.Future есть ограничения:

  • Нет обратных вызовов — необходимо блокировать выполнение с помощью get()
  • Нельзя выстраивать цепочки преобразований
  • Нельзя объединять несколько отложенных результатов

Используйте CompletableFuture (в следующем курсе) для реактивной композиции без блокировки.

Быстрая проверка

Задача выбрасывает исключение. Какое исключение возникает при вызове future.get()?

Итоги: отложенный результат и обработка ошибок

Основные выводы:

  • future.get() блокирует выполнение; всегда используйте вариант с ограничением времени
  • ExecutionException оборачивает исключения задачи; извлекайте исходное исключение с помощью getCause()
  • cancel(true) отправляет прерывание; используйте isDone и isCancelled для проверки состояния
  • ExecutorCompletionService.take() возвращает отложенные результаты в порядке завершения
  • FutureTask реализует одновременно задачу без результата и отложенный результат

Часто задаваемые вопросы

Урок «Future и обработка ошибок» бесплатный?

Да — полный текст урока «Future и обработка ошибок» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Future и обработка ошибок»?

Используйте Future.get с ограничениями времени, обрабатывайте ExecutionException и отменяйте выполняющиеся задачи. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Future и обработка ошибок»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. ExecutorService и типы пулов потоков
  2. Отправка задач: Runnable и Callable
  3. Future и обработка ошибок
  4. ScheduledExecutorService для повторяющихся задач
← Назад к Java Academy