Java Concurrency: ScheduledExecutorService — выполняем задачи по расписанию
Представьте, что нам нужно выполнять определённое действие не один раз, а через заданные промежутки времени.
Например:
- каждые 5 секунд проверять состояние сервиса;
раз в минуту отправлять статистику;
- периодически очищать устаревшие данные;
выполнять мониторинг Kafka Consumer;
- проверять доступность внешнего API.
Конечно, можно написать бесконечный цикл с Thread.sleep(). Но в Java есть более удобный инструмент — ScheduledExecutorService.
🔹 1. Что это такое?
ScheduledExecutorService — интерфейс из пакета java.util.concurrent, который позволяет запускать задачи с задержкой или периодически.
Создадим планировщик:
ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
Здесь мы создаём пул из двух потоков, которые будут выполнять запланированные задачи.
🔹 2. Однократный запуск с задержкой
Метод schedule() позволяет выполнить задачу через определённое время.
scheduler.schedule(
() -> System.out.println("Task executed"),
5,
TimeUnit.SECONDS
);
Задача выполнится один раз через 5 секунд.
🔹 3. Периодическое выполнение
Для регулярного выполнения есть два интересных метода.
scheduleAtFixedRate()
scheduler.scheduleAtFixedRate(
() -> System.out.println("Checking service..."),
0,
5,
TimeUnit.SECONDS
);
Запускает задачу с фиксированным периодом между плановыми запусками.
Например, если задача выполняется 2 секунды, следующий запуск планируется через 3 секунды после её завершения.
scheduleWithFixedDelay()
scheduler.scheduleWithFixedDelay(
() -> System.out.println("Cleaning data..."),
0,
5,
TimeUnit.SECONDS
);
Здесь задержка в 5 секунд отсчитывается после завершения предыдущего выполнения.
В чём разница?
scheduleAtFixedRate — ориентируется на расписание.
scheduleWithFixedDelay — выдерживает паузу между завершениями и следующими запусками.
🔹 4. Как остановить планировщик?
Не забываем освобождать ресурсы:
scheduler.shutdown();
Если нужно отменить конкретную задачу, методы планировщика возвращают ScheduledFuture, у которого есть метод cancel().
🔹 5. Важный нюанс, о котором часто забывают
Если периодическая задача выбросит необработанное исключение, её дальнейшие запуски прекращаются.
Поэтому в реальных приложениях важно обрабатывать исключения внутри задачи.
Также помните: если задача выполняется дольше запланированного периода, новый запуск не будет выполняться параллельно с предыдущим запуском этой же задачи.
Где это пригодится Middle/Senior Java-разработчику?
ScheduledExecutorService часто используют для фоновых операций, периодического мониторинга, очистки данных и технического обслуживания приложения.
Но не путайте его с полноценным планировщиком бизнес-процессов: для сложных расписаний и распределённого выполнения могут потребоваться другие инструменты.