Baeldung의 이 글을 정리 및 추가 정보를 넣은 글입니다.

1. Overview

  • @Scheduled annotation에 대해서 알아볼 거다. javadoc

  • method 위에 쓰이는 annotation이며 해당 method, 즉 task가 실행 예정 즉 scheduling이 되도록 설정할 때 보통 쓰인다.

  • 이 때 annotation을 쓸 method가 가져야 할 조건이 몇가지 있는데

    • void return type을 가지는걸 권장한다. 아니어도 되긴 하는데 이 경우 method를 schedule한 scheduler이 반환 값을 무시된다.
    • method가 argument를 받아서는 안된다. 즉 함수 정의에 parameter이 존재해서는 안된다.

다만 reactive Publisher을 반환하거나 Publisher로 적용이 가능한 type을 반환하는 경우에는 그 반환값이 유효하다. Publisher은 publish-subscribe 관련 메커니즘에서 유효한 녀석인데 여기서는 자세히 안 다루겠다.

2. Enable Support for Scheduling

  • 이 annotation을 사용하려면 scheduling을 사용하겠다고 표기를 해야 한다. @EnableScheduling이라는 annotation이 이 역할을 한다. javadoc

  • 해당 annotation은 @Configuration이 달린 class이랑 같이 쓰여야 한다고 명시. 다만 다른 곳에서 쓰는 것이 불가능한지는 잘 모르겠다.

  • 저 annotation을 사용하고 application 실행시, 사용자가 정의한 TaskScheduler을 우선으로 사용하기 위해 Spring에서 저 인터페이스를 구현한 코드 영역을 찾는다.

  • 만약 없는 경우 ScheduledExecutorService라는 인터페이스를 구현한 코드 영역을 탐색, 그것도 없는 경우 싱글-스레드의 기본 스케쥴러를 만든 다음에 registrar에 등록한다.

  • 또 매우매우 유의해야하는 부분으로, @EnableScheduling은 local한 application context에서만 적용이 가능하다. 이는 각기 다른 레벨의 영역(root level, servlet level 등)에서 각기 다른 bean scheduling이 가능하도록 하기 위해서다. 그래서 각 context마다 @EnableScheduling annotation을 달아야 한다. 웹 애플리케이션을 예로 들면 root web application context와 각 DispatcherServlet application context마다 @EnableScheduling을 설정해야 전부 개별적으로 scheduling이 적용된다. 물론 통합해서 스케쥴링을 할거면 이럴 필요는 없지만.

  • XML에서 설정하는 것도 가능한데 다음을 추가하면 된다.

<task:annotation-driven>

3. Schedule a Task at Fixed Delay

  • 스케쥴링이 가능해지도록 표기를 했으면 실제로 스케쥴링을 수행해봐야 한다. 방식이 여러가지가 있는데 첫번째로 고정된 '딜레이'를 기반으로 task를 수행하는 방식이다. fixedDelay@Scheduled에 제공하면 된다.
@Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
    System.out.println(
      "Fixed delay task - " + System.currentTimeMillis() / 1000);
}
  • 애플리케이션이 시작하면 해당 작업이 먼저 schedule이 된다. 그리고 종료가 될 때까지 동일 작업이 또 schedule 되진 않는다. 종료가 되면 fixedDelay에 언급한 시간만큼 시간이 지나고, 해당 작업이 다시 schedule된다.

  • 앞의 작업이 끝나야만 다시 해당 작업을 scheduling해야 할 때 쓰인다.

시간 단위는 기본이 ms이지만 timeUnit을 사용해 수정이 가능하다. 수정을 위해 받는 type은 TimeUnit이라는 점 참고.

4. Schedule a Task at a Fixed Rate

  • 일정 주기마다 해당 task를 scheduling한다.
@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
    System.out.println(
      "Fixed rate task - " + System.currentTimeMillis() / 1000);
}
  • 그런데 유의해야하는게 있는데, 위와 같이 할 경우 1000ms마다 해당 task를 스케쥴링하는 것은 맞지만 해당 task가 parallel하게 실행되는 것은 아니다. 그냥 스케쥴링만 되는 것이지, 여전히 앞의 동일 task가 완료가 되고 나서야 이 task가 진행 된다.

  • 이를 해결하려면 @Async라는 annotation을 사용해야 한다. @EnableAsync와 같이 조합해서 다음과 같이 코드를 수정하면 이제 1000ms 마다 task가 하나씩 schedule된다. 이전 task가 완료되지 않아도 말이다.

@EnableAsync
public class ScheduledFixedRateExample {
    @Async
    @Scheduled(fixedRate = 1000)
    public void scheduleFixedRateTaskAsync() throws InterruptedException {
        System.out.println(
          "Fixed rate task async - " + System.currentTimeMillis() / 1000);
        Thread.sleep(2000);
    }

}
  • ...가 Baeldung의 글에서 나온 얘기지만 사실 슉슉 넘어간거 치고 생각보다 심오한 내용들이 많이 관여하는 부분이다. 그래서 이에 대해 자세히 설명해보도록 하겠다. 이는 뒤의 11번이랑도 연관된 내용이다.

TaskExecutor and TaskScheduler

  • 관련 documentation

  • TaskExecutor인터페이스는 JDK의 Executor이라는 인터페이스랑 매우 유사한 녀석으로, task를 실제로 수행하는 녀석이다. 우리가 '일반적으로' 여기에 기대하고 있는 것은 이 녀석이 thread pool의 역할을 하는 것이다. thread pool이 뭔지에 대해서는 이 글 참고. 그러나 이름이 Executor인 이유는 사실 이 안에 있는 녀석이 thread pool이 아니라 single-thread일 수도 있고, 심지어 thread 자체는 여러개인데 거기 안에서 완전 병렬이 아니고 동기화(synchronization)을 어느정도 고려하는 시스템일 수도 있다. 그래서 그냥 thread pool이라 부르기는 애매하기에 저 이름을 가지게 되었다. 이 개념은 Spring 2.0에서 등장했다.

  • TaskExecutor을 Spring에서 만든 이유는 다른 component에게 thread pooling에 대한 abstraction을 위의 Java 5 Executor을 사용하지 않으면서 제공할 수 있도록 하기 위해서다. 즉 Java 5를 활용하지 않고 타 component가 thread pooling을 활용할 수 있도록 하기 위해서다. 실제로 Spring의 몇몇 component들은 TaskExecutor을 활용해가지고 thread pool 기능을 Java 5 없이 사용하고 있다. Spring에서 직접 만든 것이기에 Spring 내부 기능들과 잘 호환된다는 점은 덤.

  • 오! 그러면 TaskExecutor에서 schedule된 task들을 병렬 처리하는 것인가요? 아니다. 'Schedule'되는 task의 실행, 그리고 심지어는 해당 task를 scheduling하는 것은 TaskScheduler이 전담한다. 이를 위한 고유의 thread pool도 존재한다. 앞의 TaskExecutor은 schedule되지 '않는' 비동기 병렬 처리를 담당하는 녀석이다. TaskScheduler은 Spring 3.0에서 등장했다.

  • 그러면 앞의 TaskExecutor 설명은 사실 이 글이랑 관련이 없다고 생각할 수 있지만 아니다. 이는 후술. 여튼 헷갈리지 말자.

  • 위 두 인터페이스에 대한 여러 구현체를 Spring에서 제공해주며, 거기서 제공하는 기능 외의 용도로 사용해야 하면 직접 이들을 implement하면 된다.

  • 그리고 근본적으로 이 ExecutorScheduler에 해당하는 녀석들을 만든 이유는 business domain 부분 개발 과정과 비동기 병렬 처리 / Task scheduling 및 처리 부분 관련 개발 과정을 서로 독립시키기 위해서다.

사실 이거 말고도 비동기 처리와 관련된 요소는 많이 있는데 (Timer, Trigger등) 이중 Trigger에 대해서는 뒤에 설명할 예정이다.

The reason of 4. not running as expected

  • Scheduling을 하고, 또 그 schedule된 task를 처리하는 녀석이 TaskScheduler이기 때문에 @EnableScheduling을 annotate하면 TaskScheduler이 필요하게 된다. 이를 우리가 직접 정의하지 않을 경우 (ScheduledExecutorService에 해당하는 bean이 있는지 찾은 후 그것마저 없으면) 단 하나의 thread를 가지는 기본 scheduler을 만든다. 그리고 이 녀석이 task scheduling 및 실행을 담당한다.

  • 위의 코드에서는 TaskScheduler을 딱히 명시하지 않는다. 그래서 단 하나의 스레드만 최대로 생성하는 TaskScheduler이 형성이 된다. 그리고 TaskScheduler (및 TaskExecutor)의 thread들은 할당된 작업이 완료되지 않으면 다른 작업을 시작하지 않는다. (사실 후자는 모든 thread pool들이 그렇다.) 그래서 4번의 처음 코드를 사용하면 1000ms마다 scheduling은 되지만 thread가 하나다보니 뭐가 실행중이면 이게 queue되고, 그 thread가 작업이 완료된 후에야 실행이 되는 것이다.

@EnableAsync, @Async

  • 그러면 왜 @EnableAsync@Async를 활용하면 갑자기 병렬처리가 가능해지는걸까? 딱히 TaskScheduler에 해당하는 bean을 구성한 것도 아닌데 말이다.

  • 이유는 @Async의 기본 동작이... TaskExecutor이 만약 bean으로 등록되어 있지 않으면 매번 새로운 thread를 생성하는 것이기 때문이다. 정확히는 @EnableAsync에서 먼저 자기가 사용할 thread pool이 될 TaskExecutor이나 Executor을 찾는데 없으면 SimpleAsyncTaskExecutor이라는 녀석을 thread pool로 활용하고, 이 녀석의 동작 방식이 앞에 말한것처럼 task가 주어질때마다 thread를 새로 생성하는 것이기 때문이다.

  • 여기서 유의할 점은 TaskScheduler을 형성하는 것이 아니라 TaskExecutor을 형성한다는 것이다. 이는 생각해보면 당연한데, @Async의 목적이 비동기 병렬 처리를 하겠다고 표기하는 것이고 그러면 '주기적인게 아닌' 일반적인 비동기 병렬 처리를 수행할 instance가 필요하니 TaskExecutor을 찾고, 또 없으면 생성하는 것이다.

  • 그러면 여전히 @EnableScheduling으로 인해 생성되는 TaskScheduler가 별개로 있고 그건 single-thread니까 Schedule되는 task가 single-thread TaskScheduler에서 실행되는 것이 아니냐고 할 수 있다. 거기서 scheduling을 하긴 하는데, execute를 하려는 순간 TaskExecutor측으로 넘어가게 되어버린다. 그래서 TaskScheduler 정의가 없어도 그냥 새로운 thread를 마구마구 생성하면서 비동기 병렬 수행을 하게 되는 것이다.

  • 물론 현실에서 이 방법이 좋다고 보긴 힘들다. 왜냐하면 무수히 많은 task가 갑자기 생성되면 그만큼 thread를 생성해버리니 OOM (Out of Memory)가 발생할 수 있기 때문. 그래서 thread 개수 제한이 있는 thread pool을 미리 만들어서 이 사태가 벌어지지 않도록 관리한다. 대표적인 방식이 ThreadPoolTaskExecutor 객체를 만든 다음에 setMaxPoolSize로 최대 thread 개수를 지정하는 것. 다만 앞의 경우는 예제라서 간단하게 보여주기 위해 이렇게 코딩한듯. 또 이럴 일이 없다고 확실히 보장이 되면 저대로 구현해도 상관은 없어 보인다. ThreadPoolTaskExecutor 설정이 어려운건 아닌데 약간 귀찮기 때문.

  • 그럼 결국 @Async가 하는 역할은 뭐냐고요? 이 method가 비동기 병렬 수행에 사용되는 method라는 것을 표기하는데 쓰인다. Scheduler은 그냥 이 method의 호출을 담당하는 것이고 실제로는 외부 request등의 다른 event를 통해 호출이 될 때 이게 비동기 병렬 수행이 될지 아니면 그냥 실행을 할지 정해지는것도 @Async의 여부에 달려 있다.

  • 관련 javadoc : @EnableAsync @Async

참고로 어떤 executor을 사용할지 등의 세부 설정을 해야 하면 AsyncConfigurer을 만들어야 하지만... 여기서 설명하진 않겠다.

이 글이랑 관련은 없는데 검색 과정에서 @Async 작동 원리에 대해 좀 더 자세히 분석한 글이 있다. 유익해서 여기에 남긴다.

5. Fixed Rate vs Fixed Delay

  • 결국 single-thread 환경이 아니라 가정시 fixed rate랑 fixed delay의 차이점은 작업 완료 후에 일정 시간 뒤에 새로 schedule하냐, 그냥 일정 시간이 지난 후에 schedule하냐의 차이다.

  • 전자는 dependent job, 그러니까 앞의 요청의 결과물에 의존하는 형태의 request에 유용하고 후자는 independent job, 그러니까 앞의 요청의 결과물에 의존하지 않는 형태의 request에 유용하다.

6. Schedule a Task With Initial Delay

  • 참고로 '최초' 실행시에 있을 delay를 설정하는 것도 가능하다. initialDelay를 활용.
@Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void scheduleFixedRateWithInitialDelayTask() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "Fixed rate task with one second initial delay - " + now);
}
  • 해당 작업의 수행 전에 밑작업이 어느정도 필요한 경우에 유용하다. 물론 그 밑작업이 저 시간 안에 끝난다는게 보장되어야 겠지만.

7. Schedule a Task Using Cron Expressions

  • cron expression을 활용하는 것도 가능하다. 오...
@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "schedule tasks using cron jobs - " + now);
}
  • 이건 매달 15번째 날짜 오전 10시 15분에 task를 scheduling하라는 뜻이다.

  • 이때 시간대가 매우 중요한데, 기본은 서버가 가동되는 곳의 시간대를 사용하지만 zone attribute를 사용해서 시간대를 바꾸는 것이 가능하다. 밑은 위와 동일한데 기준 시간이 파리.

@Scheduled(cron = "0 15 10 15 * ?", zone = "Europe/Paris")

cron expression

  • cron expression을 들어봤을진 모르겠다... 필자는 리눅스 데몬 구성 및 github action을 공부하다가 알게 되었는데 간단히 얘기하자면 좀 더 구체적으로 주기를 설정하는 것을 해주는 expression이다.

  • 뭐 앞에 보면 알겠지만 단순히 특정 시점으로부터 몇초 뒤를 설정해주는 것이 아니라 특정 시간에 대해서 scheduling을 해줄 수 있도록 하는 expression이다.

  • 자율성이 꽤 높다. 그래서 이를 지원한다는 것 자체가 scheduling을 하는데 매우 유용하다. 관련 글은 이 둘을 추천 : 1, 2

8. Parameterizing the Schedule

  • 현재 scheduling 방식도 괜찮지만... 스케쥴링 방식을 바꿀 때마다 프로그램 전체를 다시 컴파일하고 빌드해야 한다는 것이 문제다.

  • 이를 해결하는 방법은 여러가지가 있는데, 한가지 방법은 property에다가 scheduling 주기를 집어 넣는 것이다. 밑의 이름의 property들을 property file에 집어넣어야 한다는 점 참고.

@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")
@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")
@Scheduled(cron = "${cron.expression}")

9. Configuring Scheduled Tasks Using XML

  • 두번째는 사실 schedule 주기 뿐만 아니라 scheduling 자체를 전부 정의하는 방식인데 xml을 통해 scheduling을 정의하는 것이 가능하다.

  • schedule할 method가 있는 component를 ref에, schedule 주기를 fixed-delay, initial-delay, fixed-rate, cron등을 통해, 그리고 schedule할 method를 method를 통해 지정하면 된다.

  • scheduler도 지정하는 것이 가능하다.

  • 다만 구체적인 방식은 사실 필자가 xml을 그리 선호하진 않아서 생략. 밑의 예시를 참고하자.

<!-- Configure the scheduler -->
<task:scheduler id="myScheduler" pool-size="10" />

<!-- Configure parameters -->
<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" 
      fixed-delay="5000" initial-delay="1000" />
    <task:scheduled ref="beanB" method="methodB" 
      fixed-rate="5000" />
    <task:scheduled ref="beanC" method="methodC" 
      cron="*/5 * * * * MON-FRI" />
</task:scheduled-tasks>

10. Setting Delay or Rate Dynamically at Runtime

  • @Scheduled annotation에 있는 property들은 Spring context startup때 전부 습득하고 초기화되기 때문에 이후 런타임 때 바꾸는 것이 가능하진 않다.

  • 그런데 꼭 저렇게 일정한 '규칙'을 가지고 동일 task를 scheduling하는 것이 아니라, 동적인 규칙을 통해 task를 scheduling하는게 필요할 수 있다. 이 때 SchedulingConfigurer을 사용한다.

  • 다음 코드를 보고, 어떻게 사용하는건지 천천히 알아보도록 하자.

@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {

    @Autowired
    private TickService tickService;

    @Bean
    public Executor taskExecutor() {
        return Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
        taskRegistrar.addTriggerTask(
          new Runnable() {
              @Override
              public void run() {
                  tickService.tick();
              }
          },
          new Trigger() {
              @Override
              public Date nextExecutionTime(TriggerContext context) {
                  Optional<Date> lastCompletionTime =
                    Optional.ofNullable(context.lastCompletionTime());
                  Instant nextExecutionTime =
                    lastCompletionTime.orElseGet(Date::new).toInstant()
                      .plusMillis(tickService.getDelay());
                  return Date.from(nextExecutionTime);
              }
          }
        );
    }

}

SchedulingConfigurer

  • javadoc

  • 인터페이스. Spring에서 제공한다. @EnableScheduling이 annotate되어 있는 @Configuration class에서 implement할 수 있는 인터페이스다. 위의 경우도 보면 해당 사항을 만족.

  • 용도는 다음과 같다.

    • scheduled task를 수행할 때 사용할 TaskScheduler bean을 설정할 때 사용. 그렇다고 이를 위해 꼭 사용할 필요는 없는게 @EnableScheduling을 하면 자동으로 사용할 TaskScheduler을 찾긴 한다. 더 구체적으로 명시가 필요하면 이 용도로 활용해도 된다.
    • @Scheduled annotation을 통해 '선언' 형태로 schedule될 task를 등록하는 것이 아니라 '프로그래밍'형태로 schedule될 task를 등록할 때 사용. 즉 @Scheduled annotation 외의 방식으로 동적인 주기의 scheduling task를 설정할 때 사용된다.
  • 우리는 후자의 이유로 사용하는 것이 크지만, 위의 코드의 경우 전자의 목적으로 사용되는 taskExecutor이라는 method도 존재한다. 여기서 헷갈릴 수 있는 부분이 잇는데 TaskScheduler을 등록하는데 왜 Executor을 반환하냐고 할 수 있다.

  • 뭐 실제로 Spring에서 저 용도로 사용되는게 일반적으로 좋다고 보긴 힘들지만 틀린것은 아니다. 사실 실제 사용되는 scheduler들은 Executor도 같이 implement하는 경우가 많다. 이유는 본인이 schedule한 task를 또 직접 실행해야 하기 때문이다. 그리고 여기서 Executor에 사용하는 instance는 newSingleThreadScheduledExecutor에서 생성되는 executor인데 이게 scheduling과도 관련된 instance여서 문제는 없다. 다만 전부 Java 관련이고 Spring 관련 instance는 아니어서 그리 좋은 습관은 아니라고 볼 수 있다.

configureTasks

  • 앞의 SchedulingConfigurer은 단 하나의 method만 impelemnt하면 된다. 그게 이 method다.

  • 이 method에서 우리가 사용할 scheduler을 등록하고, 또 어떻게 동적으로 task를 scheduling할지 그 규칙을 등록한다. 등록하는 곳은 ScheduledTaskRegistrar이라는 곳이다. 저건 그냥 task registering을 도와주는 bean이라는 것만 알면 된다. (javadoc)

  • scheduler 등록은 보다시피 setScheduler을 활용

  • 동적 task scheduling 관련 규칙은 보다시피 addTriggerTask라는 것을 활용하는데, 거기에 Runnable instance랑 Trigger instance를 argument로 집어넣고 있다.

Runnable

  • javadoc

  • Runnable은 java에서 제공하는 인터페이스다. 이 interface를 구현한 instance를 활용해서 thread를 형성 후 실행시 해당 instance의 run method가 실행되게 된다.

  • 예제의 run method를 보면 tickService.tick()을 하고 있다. tickService에서 주입받은 TickService의 정의는 Baeldung 글에서 참고하라는 github repository에 있다. 결론만 말하면 저 코드는 그냥 현재 시간을 초단위로 print하는 method다.

  • 여튼 이 Runnable instance는 schedule되는 task에서 실제로 수행하는 task를 지칭하는 instance다.

Trigger

  • javadoc

  • 이게 사실 중요하다. Spring에서 제공하는 인터페이스다. doc을 보면 두개의 method가 있는데 nextExecutionnextExecutionTime이다...이다만 사실 nextExecutionTime은 Spring 6.0이후로 deprecated되었다. 즉 위의 코드는 사실 deprecated된 code. nextExecution이 그 대체로 들어왔다.

  • 일단 하는건 비슷하니 설명하자면, TriggerContext를 기반으로 해당 task가 schedule될 다음 시간을 지정하는 것이다. TriggerContext에는 보통 task의 마지막 실행때 걸린 시간 및 완료 시간 등의 정보가 들어가 있다.

  • 위의 코드는 완료 시간을 기준으로, 혹은 완료 시간이 없으면 현재 시간을 기준으로 tickService.getDelay()를 더한 시간 후에 task가 실행되도록 scheduling을 하고 있다. getDelay method 부분도 Baeldung의 github repository를 참고해야 하는데 delay를 1, 2, 3...초의 형식으로 늘리고 있다.

  • OptionalDate가 뭔지는 알거라고 가정하고... Instant가 뭐냐고 할 수 있는데, 이 글을 참고하자. 그리고 nextExecution method는 사실 Date 객체를 반환하는게 아니라 Instant를 반환하도록 개선된 method다. 그래서 위와 같은 복잡한 변환 작업을 할 필요가 없고 계산한 것을 그대로 반환하면 된다.

  • addTriggerTask를 통해, Runnable instance에 해당하는 작업이 Trigger에서 정의한 방식으로 schedule되도록 설정해서 매 schedule마다 다른 주기를 활용하도록 하는 것이 가능하다.

  • ScheduledTaskRegistrar에 task를 등록하는 방법은 이외에도 많으니 참고. javadoc

11.Running Tasks in Parallel

  • 앞에서 말했지만 TaskScheduler을 명시하지 않고 @EnableAsync도 없으면 single-thread에서 schedule된 task들이 실행된다.

  • 이를 해결하기 위해서는... @EnableAsync를 사용하거나 (앞에서 보임) TaskExecutor을 직접 정의하는 방법이 있지만... (앞에서 설명) TaskScheduler을 만드는 방법도 있다. 어찌보면 이게 불가능하면 이상한거긴 하다. 밑의 코드를 한번 보자. ThreadPoolTaskScheduler의 instance를 만들어서 환경 설정 후 내뱉는 코드다. 해당 bean을 만드는 method라고 볼 수 있다.

@Bean
public TaskScheduler  taskScheduler() {
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    threadPoolTaskScheduler.setPoolSize(5);
    threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
    return threadPoolTaskScheduler;
}
  • 여기 보면 setPoolSize라는 method가 있다. 사실 여기서 사용한 (그리고 일반적으로 자주 사용하는) ThreadPoolTaskScheduler은 내부에 Java library에서 제공하는 scheduler이랑 executor의 일종인 ScheduledExecutorServiceScheduledThreadPoolExecutor을 보유한다. 그중 ScheduledThreadPoolExecutormaxPoolSize를 설정하는 것이 저 함수다.

  • 이 글에서 자세히 설명하진 않았지만 참고하면 좋다고 앞에서 소개한 글에선 TaskExecutormaxPoolSize라는 method가 있는데 그거랑 유사하다고 보면 된다. 다만 위의 코드의 경우는 본인이 내장한 executor의 maxPoolSize를 설정하는 것이고 지금 말하는 TaskExecutor의 method는 본인의 maxPoolSize를 설정하는 것이다.

  • 여하튼, 앞에서 보인 두 방식 말고도 이렇게 TaskScheduler bean을 형성하는 method를 만들고 설정해가지고 schedule되는 task들이 비동기 병렬 처리가 되도록 하는 것이 가능하다.

thread pool 관련

  • 이거랑 관련해서 검색하다 재밌는걸 찾아서 여기에 남긴다.

  • thread pool과 관련된 요소로 max pool size, core pool size, running thread, queue size가 있다. 이들의 상호작용이 생각보다 비직관적이다.

  • running thread가 core poolsize보다 많거나 같은 상황에 max pool size보다 작은 경우, queue가 꽉 찬 경우에만 새로운 thread가 생성된다. 그러지 않은 경우에는 queue에 들어간다.

  • 이거랑 관련된 stackoverflow 글

11.1 Using Spring Boot

  • Spring Boot는 굳이 저러지 않고, 다음과 같은 property를 집어넣어도 위와 동일한 효과를 내는 것이 가능하다. 와!
spring.task.scheduling.pool.size=5
  • 진짜 저 줄만 넣고, TaskScheduler에 대한 코드를 넣지 않아도 동일한 효과른 내는 것이 가능하다. 역시 Spring Boot(...)
profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글