프로그래밍/Java

Java Virtual Thread (12), custom Executor, Scheduler

개발정리 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 로 사용한다.