
Spring에서는 비동기(Asynchronous) 작업을 다양한 방식으로 처리할 수 있습니다. 대표적으로 다음과 같은 Executor 구현체들이 제공됩니다.
| 대표적인 Executor 구현체 |
|---|
| SimpleAsyncTaskExecutor |
| ThreadPoolTaskExecutor |
| ThreadPoolTaskScheduler |
| 기타 ForkJoinPool 등 |
그 중 스케줄링에 특화된 ThreadPoolTaskScheduler를 살펴보겠습니다.
스케줄링과 비동기 실행을 사용하려면 Spring에서 두 가지 애노테이션을 활성화해야 합니다.
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class SchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerApplication.class, args);
}
}
@EnableAsync: 비동기 메서드 실행을 활성화
@EnableScheduling: 스케줄링 기능을 활성화
Spring Boot는 기본적으로 taskScheduler Bean을 자동 등록해줍니다. 하지만 스레드 풀의 개수나 스레드 이름 접두사 등 커스터마이징이 필요하다면 직접 설정해주는 것이 좋습니다.
@Configuration
public class AsyncConfig {
@Bean(name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // 최대 스레드 개수
scheduler.setThreadNamePrefix("Sched-"); // 스레드 이름 접두사
scheduler.initialize();
return scheduler;
}
}
-> taskScheduler라는 이름으로 Bean을 등록하면, Spring이 @Scheduled 애노테이션 기반 작업에 이 스레드 풀을 자동으로 사용합니다.
poolSize를 제한해두면 불필요하게 많은 스레드가 생성되는 것을 방지할 수 있습니다.
( 실제 운영 환경에서는 스케줄링 작업이 많은 리소스를 차지할 수 있으므로, 스레드 풀 크기를 적절히 조정하는 것이 중요 )
SpringBoot의 Scheduled의 기본 TaskScheduler와 커스텀한 ThreadPoolTaskScheduler는 다릅니다
Spring Boot는 특별한 설정이 없을 때 내부적으로 ConcurrentTaskScheduler를 기본 등록하며, 구현체는 사실상 단일 스레드 기반으로 동작합니다.
하나의 스케줄링 작업이 오래 걸리면, 다른 스케줄링 작업들이 밀릴 수 있습니다.
그래서 주기적인 작업이 필요한 것은 별도의 스레드로 생성하여 작업하기 위하여 ThreadPoolTaskScheduler로 따로 스레드 관리하는 형식으로 진행됩니다
ex) ThreadPoolTaskScheduler 필요 예시
스케줄링할 메서드에는 @Scheduled 애노테이션을 붙입니다.
@Slf4j
@Component
public class ScheduledJobs {
/** 5초마다 실행 (이전 실행 종료와 상관없이 고정 주기) */
@Scheduled(fixedRate = 5000)
public void fixedRateJob() {
log.info("[fixedRateJob] running on thread={}", Thread.currentThread().getName());
}
/** 이전 실행이 끝난 후 10초 뒤에 재실행 */
@Scheduled(fixedDelay = 10000)
public void fixedDelayJob() {
log.info("[fixedDelayJob] running on thread={}", Thread.currentThread().getName());
}
/** 매 분 0, 20, 40초에 실행 */
@Scheduled(cron = "0,20,40 * * * * *")
public void cronJob() {
log.info("[cronJob] running on thread={}", Thread.currentThread().getName());
}
}
@Scheduled 옵션 정리
이에 대하여 동시에 같은 스레드를 사용하지 않고 별도의 스레드들로 작업되는지를 확인해보았습니다

실행해보면 각 작업이 Sched-1, Sched-2 와 같은 이름의 별도 스레드에서 수행되는 것을 확인할 수 있습니다.