Какие интерфейсы в Java используются для создания задач, выполняемых в потоках?

«Какие интерфейсы в Java используются для создания задач, выполняемых в потоках?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основные интерфейсы — Runnable и Callable<V>.

1. Интерфейс Runnable

  • Определяет задачу без возвращаемого значения и без возможности проброса проверяемых исключений.
  • Используется с Thread или ExecutorService.
Runnable task = () -> {
    System.out.println("Задача выполняется в потоке: " + Thread.currentThread().getName());
};
// Запуск через Thread
new Thread(task).start();
// Или через ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(task);

2. Интерфейс Callable<V>

  • Определяет задачу, которая возвращает результат типа V и может бросать проверяемые исключения.
  • Используется только с ExecutorService, который возвращает объект Future<V> для получения результата.
Callable<Integer> computation = () -> {
    TimeUnit.SECONDS.sleep(1);
    return 42;
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> futureResult = executor.submit(computation);

// Блокирующее получение результата
Integer result = futureResult.get();
System.out.println("Результат: " + result); // Результат: 42

Ключевое отличие: Callable предпочтительнее, когда задача должна вернуть результат или требует обработки проверяемых исключений в рамках механизма Future.