Когда использовать Thread.start(), а когда ExecutorService.submit() в Java?

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

Ответ

Выбор зависит от уровня контроля и требований к управлению потоками.

Thread.start()

  • Низкоуровневый API для прямого создания и запуска потока.
  • Использовать: Для простых, единичных задач, где не требуется пул потоков.
  • Недостатки: Создание нового потока — дорогая операция, нет встроенного управления жизненным циклом группы потоков.
    Thread thread = new Thread(() -> {
    System.out.println("Выполняю задачу в отдельном потоке");
    });
    thread.start();

ExecutorService.submit()

  • Высокоуровневый API для работы с пулами потоков (thread pools).
  • Использовать: Практически всегда в production-коде для многозадачности.
  • Преимущества:
    • Переиспользование потоков: Снижение накладных расходов.
    • Управление ресурсами: Ограничение количества одновременных задач.
    • Возврат результата: Возвращает объект Future<T> для получения результата или исключения.
    • Грамотное завершение: Методы shutdown()/shutdownNow().
// Создание пула из 4 потоков
ExecutorService executor = Executors.newFixedThreadPool(4);

// Отправка задачи на выполнение
Future<Integer> future = executor.submit(() -> {
    // Длительная операция
    return 42;
});

// Получение результата (блокирующий вызов)
Integer result = future.get();

// Обязательно завершаем сервис
executor.shutdown();

Рекомендация: В современных приложениях предпочтительнее использовать ExecutorService и его реализации (ForkJoinPool, ScheduledThreadPoolExecutor), так как это обеспечивает лучшую производительность, управляемость и соответствует best practices.