Java – run each spring scheduler in its own thread
I have multiple components with @ scheduled annotation, and I find that spring starts only one at a time, even if they are scheduled to run at the same time
My use case is as follows I want each @ scheduled annotation to run in its own thread, but each thread only runs once
Given this pseudo code with two schedulers:
@Scheduled(cron = "0 * * * * *") //run every minute
public void methodA() {
log.info("Running method A");
executeLongRunningJob("Finished method A");
}
@Scheduled(cron = "0 * * * * *") //run every minute
public void methodB() {
log.info("Running method B");
executeLongRunningJob("Finished method B");
}
private void executeLongRunningJob(String msg) {
Thread.sleep(70 seconds);
System.out.println(msg);
}
Note that this task takes longer than the scheduled scheduler to run This is crucial I don't want the scheduler to restart before it finishes running
Running this code out of the box gives me the following output:
Running method A Finished method A Running method B Finished method B Running method A Finished method A Running method B Finished method B ... and so on
Obviously, it runs two schedulers in one thread
When I put @ async on my expensive method, I almost got the right behavior, except that I didn't complete the expensive method before the new scheduler started
Running method A Running method B Running method A Running method B Finished method A Finished method B ... and so on
What I want is this output:
Running method A Running method B Finished method A Finished method B Running method A Running method B Finished method A Finished method B ... and so on
How can I do that? I want each scheduler to run at the same time, but wait until it is complete before allowing it to run again Remember, I have more than two schedulers running at the same and sometimes different times
Solution
You're right - by default, the scheduler uses a thread pool of size 1, so each task is processed sequentially You can do this by configuring the taskscheduler bean with the required pool size Consider the following examples:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.Date;
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@Bean
public TaskScheduler taskScheduler() {
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
return scheduler;
}
@Scheduled(fixedDelay = 2 * 1000L,initialDelay = 3 * 1000L)
public void scheduled1() throws InterruptedException {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ": scheduled1");
Thread.sleep(1000);
}
@Scheduled(fixedDelay = 3 * 1000L,initialDelay = 3 * 1000L)
public void scheduled2() throws InterruptedException {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ": scheduled2");
Thread.sleep(1000);
}
}
It will run each scheduled task in a separate thread, for example:
Tue Jul 18 20:21:50 CEST 2017 taskScheduler-1: scheduled2 Tue Jul 18 20:21:50 CEST 2017 taskScheduler-2: scheduled1 Tue Jul 18 20:21:53 CEST 2017 taskScheduler-1: scheduled1 Tue Jul 18 20:21:54 CEST 2017 taskScheduler-3: scheduled2 Tue Jul 18 20:21:56 CEST 2017 taskScheduler-2: scheduled1 Tue Jul 18 20:21:58 CEST 2017 taskScheduler-4: scheduled2 Tue Jul 18 20:21:59 CEST 2017 taskScheduler-1: scheduled1
