I am configuring a scheduled task to run in different threads. Here is the configuration code
@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 code that uses it
@Scheduled(fixedRateString = "2000" )
public void testaMethod() {
log.debug("here is the message");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I am sleeping the thread for 10s with fixedRate of 2s. so I expect to see different threads in the log but I see only one
logs are here
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message"}
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message"}
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message"}
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message"}
⚡Dual Threaded ApplicationsAn ExecutorService produced by the Executors. newSingleThreadExecutor() static method can be used to create a single additional thread. The ExecutorService includes the execute() method, which accepts any class implementing the Runnable interface or a lambda expression as an input.
@Scheduled causes the code to be run in a separate thread, not necessarily new as it might come from a thread pool.
By default, Spring uses a local single-threaded scheduler to run the tasks. As a result, even if we have multiple @Scheduled methods, they each need to wait for the thread to complete executing a previous task.
To run asynchronous tasks in your Spring Boot application by using CICS enabled threads, there are two options. You can either set an Asynchronous Executor for the whole application, or you can choose to specify an AsyncExecutor on a per method basis.
@Scheduled(fixedRateString = "2000" )
does not gurantee testaMethod
be executed every 2 seconds, if the time cost is more than 2s, such as Thread.sleep(10000)
, the new task will be put in a queue. Only when the old one has been executed, the scheduler will fetch the new task from queue and execute it. Since the new task is now the only task in the scheduler, it could be run without creating a new Thread
.
To solve this problem, you can combile @Async and @Scheduled
SchedulerConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setTaskScheduler(poolScheduler());
}
@Bean
public TaskScheduler poolScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("poolScheduler");
scheduler.setPoolSize(POOL_SIZE);
return scheduler;
}
}
SchedulerTask
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SchedulerTask {
@Autowired
private AsyncTask asyncTask;
@Scheduled(fixedRateString = "2000" )
public void testaMethod() {
asyncTask.asyncMethod();
}
}
AsyncTask
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
@Component
@EnableAsync
public class AsyncTask {
private Log log = LogFactory.getLog(AsyncTask.class);
@Async
public void asyncMethod() {
log.debug("here is the message");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Test results
11:20:54.682 [poolScheduler1] DEBUG com.test.AsyncTask - here is the message
11:20:56.668 [poolScheduler3] DEBUG com.test.AsyncTask - here is the message
11:20:58.668 [poolScheduler2] DEBUG com.test.AsyncTask - here is the message
11:21:00.669 [poolScheduler6] DEBUG com.test.AsyncTask - here is the message
11:21:02.669 [poolScheduler7] DEBUG com.test.AsyncTask - here is the message
11:21:04.669 [poolScheduler4] DEBUG com.test.AsyncTask - here is the message
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With