spring-bootscheduled-tasksthreadpoolexecutorspring-scheduledspring-async

@Scheduled and @Async are sharing same threadpool in spring-boot


I have configured two different thread pools, one for @Scheduled and other for @Async. However, I notice that the thread-pool for @Async is not being used.

Here is the Scheduler configuration

@Configuration
@EnableScheduling
public class SchedulerConfig 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-sched-pool-");
        threadPoolTaskScheduler.initialize();
        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}

Here is the Configuration for Async

@Configuration
@EnableAsync
public class AppConfig {

 @Bean(name = "asyncTaskExecutor")
    public TaskExecutor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(15);
        executor.setMaxPoolSize(15);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("my-async-pool-");
        executor.initialize();
        return executor;
    }
}

Here is how I invoke them

@Scheduled(fixedRateString = "2000" )
    public void schedule() {
      log.debug("here is the message from schedule");
      asyncMethod();
    }

@Async("asyncTaskExecutor")
public void asyncMethod(){
  log.info("here is the message from async");
}

Here are the logs

{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message from schedule"}
{"thread":"my-sched-pool-1","level":"INFO","description":"here is the message from async"}

As you can notice, both logs are having same pool of that scheduler. but I expect to see the second one to come from async


Solution

  • If you call @Async methods from the same class they are declared you are effectively bypassing Spring's proxy mechanism and that is why your example is not working. Try calling the method from a separate class annotated with @Service or any of the other @Component types.

    @Service
    SomeScheduledClass {
    
      private final SomeAsyncClass someAsyncClass;
    
      public SomeScheduledClass(SomeAsyncClass someAsyncClass) {
        this.someAsyncClass = someAsyncClass;
      }
    
      @Scheduled(fixedRateString = "2000" )
      public void schedule() {
        log.debug("here is the message from schedule");
        someAsyncClass.asyncMethod();
      }
    }
    
    @Service
    SomeAsyncClass {
      @Async("asyncTaskExecutor")
      public void asyncMethod(){
        log.info("here is the message from async");
      }
    }