[Spring] @Scheduled 사용

2021. 12. 6. 17:12개발노트

특정 기간, 원하는 주기에 따라 작업을 실행하기 위해 @Scheduled 가 사용된다.

@Scheduled 를 사용하기 위해서는 관련 클래스에 @EnableScheduling 을 추가해야한다.

 

 

 

@Scheduled 가 부텨되는 메서드는 파라미터를 가질 수 없고 반드시 void 리턴 타입이어야 하며

아래 세가지 작업을 수행 할 수 있다.

  • fixedDelay: 이전 작업이 끝난 후 동작
  • fixedRate: 이전 작업이 끝나는 것과 별개로 일정한 시간 간격으로 실행
  • cron:  크론 주기에 따라 실행

 

cron

@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(cron = "1 * * * * *") // 1분 주기
    public void cronJob() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("strDate = " + strDate);

    }

}

@EnableScheduling 사용으로 스프링에게 @Scheduled 사용을 알리고  위와 같이 크론 주기에 따라 코드를 실행 할 수 있다.

// OutPut
strDate = 2021-12-07 14:08:01.002
strDate = 2021-12-07 14:09:01.003

 

fixedDelay

@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(fixedDelay = 1000) // 작업 후 1초 후 실행
    public void fixedDelayJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("FixedDelayJob = " + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(4000);

    }
}

4초 동안 sleep 하기 때문에 5초 후에 다음 작업이 실행된다.

// OutPut
FixedDelayJob = 2021-12-07 14:06:52.925
scheduling-1
FixedDelayJob = 2021-12-07 14:06:57.927
scheduling-1
FixedDelayJob = 2021-12-07 14:07:02.931
scheduling-1

fixedRate

@EnableScheduling
@Component
public class Scheduler {

    @Scheduled(fixedRate = 1000) // 1초 간격 실행
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("FixedDelayJob = " + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(2000);

    }

}

2초 동안 sleep 하지만 작업이 시작된 시점부터 1초 간격으로 실행 되기 때문에 2초가 지난 후 바로 실행된다.

// OutPut
FixedDelayJob = 2021-12-07 14:05:42.568
scheduling-1
FixedDelayJob = 2021-12-07 14:05:44.573
scheduling-1
FixedDelayJob = 2021-12-07 14:05:46.575
scheduling-1

실제로 1초 간격으로 실행되기 위해서는 비동기화가 필요하다.

 

비동기화

이제 fixedRate 코드 메서드에 @Async 를 붙여주면 비동기 작업을 지원해 일정 간격으로 실행된다.

하지만 작업이 길어 쓰레드가 모두 사용중이라면 대기 시간을 가지게 된다.

@Component
@EnableScheduling
@EnableAsync
public class Scheduler {

    @Scheduled(fixedRate = 100) // 1초 간격 실행
    @Async
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("FixedDelayJob = " + strDate);
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(3000);

    }

}

 @Async 사용을 위해 @EnableAsync 어노테이션으로 스프링에게 비동기 사용을 알린다.

 

// OutPut
FixedDelayJob = 2021-12-07 15:42:00.487
task-1
FixedDelayJob = 2021-12-07 15:42:01.481
task-2
FixedDelayJob = 2021-12-07 15:42:02.478
task-3
FixedDelayJob = 2021-12-07 15:42:03.478
task-4
FixedDelayJob = 2021-12-07 15:42:04.478
task-5

 

 

여러 스케줄 동시 작업

기본적으로 스케쥴이 사용하는 스레드는 하나이므로 수행해야 할 작업이 여러개라면 스레드 락이 걸릴 수 있다.

따라서 아래와 같이 스레드풀을 사용하도록 설정한다.

@Configuration
public class SchdulerConfig implements SchedulingConfigurer {

    private final int POOL_SIZE = 10;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(POOL_SIZE); // 대기하는 스레드 개수
        threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-"); // 스레드 이름
        threadPoolTaskScheduler.initialize();
        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }

}

아래와 같이 사용한다.

@Component
@EnableScheduling
public class Scheduler {

    @Scheduled(fixedDelay = 1000) // 작업 후 1초 후 실행
    public void fixedDelayJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("FixedDelayJob = " + strDate);
        System.out.println(Thread.currentThread().getName());
    }

    @Scheduled(fixedRate = 1000) // 1초 간격
    public void fixedRateJob() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Date now = new Date();
        String strDate = sdf.format(now);
        System.out.println("FixedDelayJob = " + strDate);
        System.out.println(Thread.currentThread().getName());
    }

}
// OutPut
FixedDelayJob = 2021-12-07 15:56:42.973
jin-Scheduling2
FixedDelayJob = 2021-12-07 15:56:42.973
jin-Scheduling1
FixedDelayJob = 2021-12-07 15:56:43.973
jin-Scheduling3
FixedDelayJob = 2021-12-07 15:56:43.974
jin-Scheduling4
FixedDelayJob = 2021-12-07 15:56:44.973
jin-Scheduling2
FixedDelayJob = 2021-12-07 15:56:44.974
jin-Scheduling5
FixedDelayJob = 2021-12-07 15:56:45.975
jin-Scheduling6