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