Недавно отвечал на вопрос почему аннотации @Scheduled и @Async иногда используют вместе, данный вопрос попался человеку на собеседовании. Многие начинающие разработчики на java не до конца понимают в каких потоках происходит выполнение программы в таком случае. В данном материале постараюсь объяснить зачем аннотации @Scheduled и @Async ставят вместе, какая проблема при этом решается, в каких потоках происходит работа программы и как делать правильно. Подписывайтесь на мой блог в телеграм, где я раньше всего публикую материалы. Допустим вам необходимо выполнять какое-то действие раз в минуту, например ходить в базу и смотреть есть ли там записи по которым не выполнилась отправка во внешнюю систему и если такие есть то выполнить отправку повторно (доотправка). И еще одно действие, которое надо выполнять раз в день, например сформировать аналитический отчет, и формироваться он будет очень долго, целых 5 минут. Действия которые нужно выполнять по времени я буду называть джобами (от слова job). Это вполне реальная ситуация на коммерческом проекте. И мы начали замечать, что во время формирования отчета (а мы помним что это длится 5 минут), первый джоб доотправки не выполняется, почему так присходит? Представим эту ситуацию в коде. Пометим два метода аннотацией @Scheduled: первый будет выполняться раз в секунду и выводить в лог текущее время и имя потока; второй будет запускаться раз в 5 секунд и выводить в лог текущее время, имя потока и вдобавок зависать на 3 секунды, имитируя тяжеловесную операцию. Вывод в консоль: Разберем что мы увидели в логе. Оба метода выполняются в одном и том же потоке. При этом пока 3 секунды выполняется тяжеловсный джоб, второй джоб не выполняется вовсе, потому что единственный поток занят. На коммерческом проекте это может стать проблемой, и у вас уйдет много времени чтобы разобраться почему ваш @Scheduled метод не отрабатывает в заданное время. Решение - передать выполнение джобов выделенному пулу потоков. И есть несколько вариантов передачи выполнения пулу: настроить аннотацию @Scheduled чтобы метод помеченный данной аннотацией всегда выполнялся в отдельном потоке; явно передавать выполнение кода в отдельный поток, например с помощью  аннотации @Async; Рассмотрим оба варианта. Если обратиться к javadoc аннотации @EnableScheduling то найдем пример конфигурации: Добавим её в наше приложение и перезапустим. Вывод в лог: Видим, что запуск каждого джоба выполняется в отдельном потоке, при этом тяжеловесный джоб не мешает запуску легковесного. Проблема решена. Поставим аннотацию @Async над @Scheduled методами: Вывод в лог будет аналогичен предыдущему примеру, т.е. джобы будут выполняться в отдельных потоках и не мешать друг другу. При использовании аннотации @Async по умолчанию Spring создает Executor, у которого нет предела по количеству создаваемых потоков. Тем самым, если ваш джоб будет запускаться чаще чем завершаться, то возникнет утечка памяти из-за постоянно создаваемых потоков. Решить проблему можно с помощью настройки Executor для @Async, пример настройки можно посмотреть в javadoc аннотации @EnableAsync. Правильная настройка Executor'ов это тема для отдельной статьи. Если у вас один джоб на всё приложение и он выполняется быстрее чем запускается, то можно ничего не делать и пользоваться аннотацией @Scheduled в том виде как она работает из коробки. Если у вас несколько джобов то лучше всегда настраивать явный планировщик задач, и для удобства пользоваться аннотацией @Async. Стоит заметить, что при использовании @Async лучше всегда указывать Executor явно, пример: тогда вы гарантируете, что потоки Executor'а не будут заняты другими задачами кроме ваших джобов.