Выбор между submit и execute в ExecutorService Java
Быстрый ответ
Если вам требуется контролировать результат выполнения задачи через объект Future, то следует использовать метод submit. Он поможет получать результаты работы или отменять задачу. Метод execute идеально подойдет для задач, у которых нет необходимости в отслеживании результата:
submit:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> "результат");
execute:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> System.out.println("Задача выполнена!"));
Подводя итог: используйте submit для возможности контроля, и execute для простоты выполнения задач без отслеживания результата.

Понимание обработки исключений
Исключения в потоке работы метода execute перехватываются UncaughtExceptionHandler. В случае с submit, любые исключения захватываются объектом Future и преобразуются в ExecutionException, когда вы пытаетесь получить результат:
try {
Future<?> future = executor.submit(() -> { throw new RuntimeException("Упс, ошибка выполнения!"); });
future.get();
} catch (ExecutionException ee) {
Throwable originalCause = ee.getCause();
}
Выбор подходящих задач
Метод execute работает с объектами Runnable, а submit может также принимать Callable. Это особенно актуально, когда задаче нужно возвращать результат или выбрасывать исключение:
Callable<Integer> task = () -> 1 + 1;
Future<Integer> future = executor.submit(task);
Осознанный выбор
Если вам не нужно знать о результатах работы задачи, и достаточно просто её выполнить, выбирайте execute. Если требуется более сложная логика управления задачами, включая отслеживание, отмену или обработку исключений, используйте submit.
Более полный контроль через переопределение
Можно расширить функционал, создав класс CustomThreadPoolExecutor и переопределив метод afterExecute для дополнительного контроля выполнения задачи:
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// Обработка исключений и других задач после выполнения
}
}
Визуализация
Если представить ExecutorService как дирижёра оркестра:
ExecutorService orchestra = Executors.newFixedThreadPool(4);
Использование execute():
orchestra.execute(new Violinist());
🎻 Музыкант начинает играть, и не ждет аплодисментов.
Использование submit():
Future<String> applause = orchestra.submit(new Violist());
🎻👏 Музыкант играет и ожидает аплодисментов.
Какой итог вы хотите получить от "исполнения" вашей задачи, исходя из этого, и выбирайте подходящий метод.
Практические примеры использования
Запуск задач без затруднений
Используйте метод submit с обёртками, чтобы предотвратить возможные трудности с выполнением вашей задачи:
executor.submit(new NonBlockingTaskWrapper<>(() -> { /* body task */ }));
Обработка пользовательских запросов
ExecutorService пригодится при работе с динамическими задачами, особенно если вам нужно обрабатывать пользовательские запросы.
Оптимизация логирования ошибок
Создание класса ExtendedExecutor с переопределением методов позволяет эффективно упорядочить обработку ошибок и оптимизировать процесс логирования.
Полезные материалы
- Вопрос на Stack Overflow о блокировке в ThreadPoolExecutor — анализ различий между
submitиexecute. - Руководство по ExecutorService от Oracle — пошаговое объяснение работы с ExecutorService.
- Общий обзор параллелизма в Java — обзор утилит для параллельной обработки данных в Java.
- Документация по ExecutorService — официальная документация Java SE.
- Выбор способа обработки многопоточности в Java — обсуждается выбор стратегий для многопоточности, что может помочь при принятии решения между
submitиexecute.


