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()); // trueisCancelled и 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()); // falseCompletionService для получения первых готовых результатов
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 — локальная установка не требуется.
Все уроки этого курса
- ExecutorService и типы пулов потоков
- Отправка задач: Runnable и Callable
- Future и обработка ошибок
- ScheduledExecutorService для повторяющихся задач