Выбор между 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
.