FutureTask

FutureTask<V> реализует одновременно Runnable и Future. Это мост между задачей, которую можно запустить, и объектом, через который получают ее результат.

FutureTask<Integer> task = new FutureTask<>(() -> calculate());
Thread thread = new Thread(task, "calculator");
thread.start();
 
Integer result = task.get();

Тот же объект можно передать в Executor.execute(task). Вызывать run() несколько раз допустимо, но вычисление выполнится только один раз: после завершения последующие вызовы ничего не пересчитают.

Вариант с Runnable

FutureTask<String> task =
        new FutureTask<>(() -> writeReport(), "written");

Здесь заданное значение "written" становится результатом после нормального завершения Runnable.

Расширение

Protected-метод done() вызывается после перехода в конечное состояние:

final class LoggedTask<V> extends FutureTask<V> {
    LoggedTask(Callable<V> callable) {
        super(callable);
    }
 
    @Override
    protected void done() {
        // Не блокироваться надолго; состояние уже terminal.
    }
}

При отмене действуют те же ограничения, что у Future: interrupt — кооперативный сигнал.

В обычном коде submit(Callable) уже создает внутреннюю FutureTask-подобную обертку, поэтому ручной класс нужен редко: когда задача должна быть одновременно передана как Runnable и опубликована как Future, либо нужен hook done().

На собеседовании

  • Какие интерфейсы соединяет FutureTask? Runnable и Future через RunnableFuture.
  • Выполнится ли Callable дважды после двух run()? Нет.
  • Когда вызывается done()? После результата, ошибки или отмены.
  • Чем FutureTask отличается от CompletableFuture? Первая оборачивает одно запускаемое вычисление, вторая предназначена для композиции стадий.