Runnable и Callable

RunnableCallable<V>
Методvoid run()V call() throws Exception
Результатнетесть
Checked exceptionобъявить нельзяможно
Отправка в executorexecute или submitsubmit

Callable не возвращает Future самостоятельно. Future возвращает ExecutorService.submit, оборачивая результат и ошибку задачи.

ExecutorService pool = Executors.newFixedThreadPool(2);
try {
    Runnable audit = () -> writeAudit();
    Callable<Integer> count = () -> countRecords();
 
    pool.execute(audit);
    Future<Integer> future = pool.submit(count);
    System.out.println(future.get());
} finally {
    pool.shutdown();
}

Исключение из Callable сохраняется в Future и при get() оборачивается в ExecutionException. Исключение из Runnable, переданного через execute, попадает в механизм uncaught exception потока; через submit оно также хранится в Future, поэтому игнорирование возвращенного объекта может скрыть сбой.

См. Объект Future.

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

  • Возвращает ли Callable.call() Future? Нет, он возвращает V; Future создает executor.
  • Как получить ошибку Callable? Через ExecutionException.getCause() после get().
  • Можно ли отправить Runnable через submit? Да, результатом будет Future<?>.
  • Что выбрать для fire-and-forget? Обычно Runnable, но ошибку все равно нужно наблюдать.