Что такое FutureTask?
FutureTask<V> – это класс, реализующий одновременно интерфейсы Future<V> и Runnable. Он представляет собой отменяемое асинхронное вычисление, которое можно запустить в отдельном потоке и получить результат, когда он будет готов.
Иерархия
Пример
Runnable ──┐
├──▶ RunnableFuture<V> ──▶ FutureTask<V>
Future<V> ─┘
Конструкторы
Пример
// Обёртка над Callable — результат берётся из call()
FutureTask<String> task1 = new FutureTask<>(() -> "Результат");
// Обёртка над Runnable — при завершении вернёт указанный result
FutureTask<String> task2 = new FutureTask<>(() -> {
System.out.println("Работаю");
}, "Готово");
Основные методы
| Метод | Описание |
|---|---|
run() |
Выполняет вычисление (из Runnable) |
get() |
Блокирует текущий поток до получения результата |
get(long timeout, TimeUnit unit) |
Блокирует с таймаутом, выбрасывает TimeoutException |
cancel(boolean mayInterruptIfRunning) |
Отменяет задачу. Если true и задача выполняется – прерывает поток |
isCancelled() |
Была ли задача отменена |
isDone() |
Завершена ли задача (успешно, с ошибкой или отменена) |
Способы запуска
Пример: три способа запуска FutureTask
public class FutureTaskDemo {
public static void main(String[] args) throws Exception {
Callable<Integer> computation = () -> {
Thread.sleep(2000);
return 42;
};
// Способ 1: через Thread
FutureTask<Integer> task1 = new FutureTask<>(computation);
new Thread(task1).start();
System.out.println("Результат (Thread): " + task1.get());
// Способ 2: через ExecutorService
FutureTask<Integer> task2 = new FutureTask<>(computation);
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(task2);
System.out.println("Результат (Executor): " + task2.get());
executor.shutdown();
// Способ 3: прямой вызов run() (в текущем потоке)
FutureTask<Integer> task3 = new FutureTask<>(computation);
task3.run(); // Блокирует текущий поток
System.out.println("Результат (run): " + task3.get());
}
}
Особенности
- Одноразовость –
FutureTaskвыполняется ровно один раз. Повторный вызовrun()не приводит к повторному вычислению. - Потокобезопасность –
get()можно вызывать из нескольких потоков одновременно; все они получат один и тот же результат. - Хранение исключения – если
call()выбросил исключение,get()обернёт его вExecutionException. - Переопределяемый
done()– защищённый методdone()вызывается после завершения задачи и может быть переопределён для получения уведомлений.
Аналогия из жизни.
FutureTask– это квитанция из химчистки: вы сдаёте вещь (задачу), получаете квитанцию (объектFutureTask), и позже приходите за результатом. Если результат ещё не готов, вы ждёте (get()блокируется). Если передумали – можете отменить заказ (cancel()), но только если вещь ещё не обработали.
На собеседовании. Покажите, что
FutureTaskобъединяетRunnableиFuture– поэтому его можно и передать вThread, и использовать для получения результата. На практике в современном коде чаще используютCompletableFuture(Java 8+), который поддерживает композицию и цепочки асинхронных операций.FutureTaskостаётся полезным для низкоуровневого контроля (например, кэширование результатов вычислений).