Запускаются ли задачи одновременно в разных потоках при использовании @Scheduled в Spring
Что проверяют
Знаете ли вы, что по умолчанию планировщик однопоточный. Это регулярно всплывает как инцидент: одна долгая задача останавливает все остальные.
Как отвечать
По умолчанию нет. Spring создаёт планировщик с пулом из одного потока, поэтому все методы с @Scheduled выполняются последовательно. Если одна задача идёт долго, остальные ждут своей очереди, даже когда их время давно наступило.
Как это меняют:
- задать размер пула свойством
spring.task.scheduling.pool.size; - объявить свой
ThreadPoolTaskSchedulerбином; - пометить метод
@Async(при включённом@EnableAsync), тогда планировщик только запускает задачу, а выполняется она в другом пуле.
Дальше стоит проговорить разницу режимов, её тоже часто спрашивают следом:
fixedDelayотсчитывает паузу от конца предыдущего выполнения, поэтому наложения не будет;fixedRateотсчитывает от начала, и если задача не уложилась в интервал, следующий запуск встанет в очередь, а при нескольких потоках выполнится параллельно с предыдущим;cronпривязан к расписанию, и там же задаётся временная зона.
Отдельно назовите то, о чём забывают почти все: в кластере из нескольких экземпляров приложения задача запустится на каждом. Если она должна выполниться один раз, нужна распределённая блокировка (ShedLock и подобные) или вынос задачи во внешний планировщик.
Где спотыкаются
Уверены, что каждая задача получает свой поток, и получают простой из-за одной медленной.
Ставят fixedRate для задачи, которая иногда не укладывается в интервал, и получают накопление очереди.
Забывают про несколько экземпляров и дважды рассылают письма пользователям.