ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java Virtual Thread (12), custom Executor, Scheduler
    프로그래밍/Java 2024. 6. 29. 12:15

     하나의 Spring Boot Application 인데, 이 안에 주문과 배송 등이 구현되어 있다고 가정해보자. 

     

     

    이 때 주문은 Virtual Thread를 사용하고, 다른 비즈니스에서는 CPU를 많이 사용하기 때문에 Platform Thread 를 사용하면서 사용할 수 있다. 만약, yml에 spring-virtual-enable-true로 설정한다면 virtual thread 로 바뀌기 때문에 Platform thread를 사용할 수 없다.

     

     

    @Configuration
    @Slf4j
    public class SchedulerConfig {
    
        @Bean
        public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
            ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
            threadPoolTaskScheduler.setThreadNamePrefix("myScheduler-");
            threadPoolTaskScheduler.setPoolSize(10);
            // TODO: more settings...
            return threadPoolTaskScheduler;
        }
    
        @Primary
        @Bean
        public SimpleAsyncTaskScheduler simpleAsyncTaskScheduler(SimpleAsyncTaskSchedulerBuilder builder) {
            return builder.build();
        }
    
    }

     

    이를 위해 virtual thread용 task scheduler Bean을 만들어주고, 플랫폼 스레드를 사용하는 thread task scheduler Bean 만들어준다. 이렇게 두 개의 Bean을 만든 다음 사용하는 곳에서 골라쓰면 되는 것이다. 

     

     

     

    1) SimpleAsyncTaskSchedulerBuilder 의 경우 virtual thread가 들어가 있는 빌드를 주입받는 것이다. 그리고 builder.build 를 통해 빈으로 등록해주면 virtual thread용 스케줄러가 된다. 

     

     

    2) ThreadPoolTaskScheduler 의 경우 ThreadPoolTaskScheduler를 만든 다음 prefix, full-size 등을 정한 다음 빈으로 등록해주면 된다. 

     

     

    이 중 어느 것을 default로 동작하게 할 것인가는 @Primary를 선언함으로써 정할 수 있다. 

     

    @Service
    @Slf4j
    public class VirtualScheduler {
    
        @Scheduled(fixedRate = 5000)
        public void fixedRate() {
            log.info("fixedRate. thread: {}", Thread.currentThread());
        }
    
        @Scheduled(fixedRate = 5000, scheduler = "threadPoolTaskScheduler")
        public void fixedRate2() {
            log.info("fixedRate2. thread: {}", Thread.currentThread());
        }
    }

     

    그러면 스케줄러에서 아무것도 안 적으면 default Primary 스케줄러가 적용된다. 만약 scheduler 에 스케줄러 이름을 적어주면 해당되는 Bean으로 동작한다.

     

     

     

    Task Executor 의 customized 또한 동일하다.

     

    @Async에는 value 속성이 있다. 이는 비동기로 동작하는데, 어떤  thread pool을 사용할 것인지를 value에 적을 수 있는 것이다. 적지 않으면 default Primary로 되어 있는 thread pool을 사용하게된다. (virtual thread pool 혹은 플랫폼 thread pool)

     

    만약 Bean 이름을 적어주면 Bean이 만든 thread pool을 사용하게 된다.

     

    @Configuration
    @Slf4j
    public class ExecutorConfig {
    
        @Bean
        public SimpleAsyncTaskExecutor taskExecutor(SimpleAsyncTaskExecutorBuilder builder) {
            return builder.build();
        }
    
        @Bean
        public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
            ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setCorePoolSize(10);
            threadPoolTaskExecutor.setThreadNamePrefix("myThreadPool-");
            // TODO: more setting...
            return threadPoolTaskExecutor;
        }
    
    }

     

    SimpleAsyncTaskExecutorBuilder 를 주입받으면 spring 은 virtual thread를 사용하는 빌더로 만들어준다. 이는 virtual thread를 사용하는 taskExecutor가 되는 것이다. 

     

     

    TaskExecutor 는 Primary 를 등록한다고 default Bean이 되지 않는다. spring에서는 TaskExecutor Bean이 두 개가 있으면 Bean 이름이 TaskExecutor 인 Bean이 제일 우선순위가 높다.

     

     

    만약 다른 Bean을 사용하고 싶다면 @Async 에  value 값으로 Bean 이름을 등록하자. 

     

     

    ... 

     

    궁금 

    executor, scheduler 뭐가 다른가 

     

    정리

     

    1) Executor, Scheduler 모두 custom Bean을 만들어 원하는 스레드를 만들 수 있다.

    2) Scheduler 는 Scheduler 어노테이션 안에, Executor 는 Executor 어노테이션 안에 원하는 Bean 이름을 등록해주면 된다.

    3) Scheduler 에 default 는 Primary 로 선언한 bean 을 사용하며, Executor 는 Bean 이름이 taskExecutor 인 Bean을 default 로 사용한다. 

Designed by Tistory.