Gymterview
middle

Что такое 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());
    }
}

Особенности

  1. ОдноразовостьFutureTask выполняется ровно один раз. Повторный вызов run() не приводит к повторному вычислению.
  2. Потокобезопасностьget() можно вызывать из нескольких потоков одновременно; все они получат один и тот же результат.
  3. Хранение исключения – если call() выбросил исключение, get() обернёт его в ExecutionException.
  4. Переопределяемый done() – защищённый метод done() вызывается после завершения задачи и может быть переопределён для получения уведомлений.

Аналогия из жизни. FutureTask – это квитанция из химчистки: вы сдаёте вещь (задачу), получаете квитанцию (объект FutureTask), и позже приходите за результатом. Если результат ещё не готов, вы ждёте (get() блокируется). Если передумали – можете отменить заказ (cancel()), но только если вещь ещё не обработали.

На собеседовании. Покажите, что FutureTask объединяет Runnable и Future – поэтому его можно и передать в Thread, и использовать для получения результата. На практике в современном коде чаще используют CompletableFuture (Java 8+), который поддерживает композицию и цепочки асинхронных операций. FutureTask остаётся полезным для низкоуровневого контроля (например, кэширование результатов вычислений).