I wrote the question for a long time and it is long. But I tried to show as much as possible what I did and what is not clear. Please finish reading and thank you for your patience!
I tried many experiments, write spring doc spring doc, (write questions in this site) But still not understand the full picture.
I have a task to implement some schedulers in one spring-boot server.
- First Scheduler will check data in DB every 1 second and run some logic.
- Second Scheduler will send requests to third-party service every 10 millisecond.
South schedulers must work with thread-pool and have different settings. For example first - 5 threads, second - 10 threads. While I understood, I tried several options and finally got confused, what should I choose and how to use it more correctly:
For test I create 2 beans with logics and will call methods from this beans every time:
@Slf4j
@Component
public class TestBean {
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("First bean print");
}
}
and
@Slf4j
@Component
public class TestBean2 {
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Second bean print");
}
}
I still do not understand the difference, what and when to use - @Scheduled annotation or TaskScheduler from code. I tried to create a method with @Scheduled annotation:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
public MyScheduler(TestBean testBean, TestBean2 testBean2) {
this.testBean = testBean;
this.testBean2 = testBean2;
}
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();//call method from first bean every 1 sec
}
}
Output log:
2018-09-05 13:17:28.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:17:37.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:17:46.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
Work one thread and print log from first bean each 9 sec. After that I add TaskScheduler:
@Bean
ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
threadPoolTaskScheduler.setThreadNamePrefix("TASK_SCHEDULER_FIRST-");
return threadPoolTaskScheduler;
}
And start app. Output:
2018-09-05 13:21:40.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:21:49.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:21:58.973 INFO 7172 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:22:07.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
Each 9 sec but different threads print log from first bean.
After that I try inject TaskScheduler and run schedule by another way:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
private final ThreadPoolTaskScheduler taskScheduler;
public MyScheduler(TestBean testBean, TestBean2 testBean2, ThreadPoolTaskScheduler taskScheduler) {
this.testBean = testBean;
this.testBean2 = testBean2;
this.taskScheduler = taskScheduler;
}
@PostConstruct
public void test() {
taskScheduler.scheduleAtFixedRate(testBean::test, 1000L);
testBean.test();
}
}
But got similar output:
2018-09-05 13:25:54.541 INFO 7044 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:03.541 INFO 7044 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:12.541 INFO 7044 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:21.541 INFO 7044 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
After that I read that I need use @Async annotation and start bean's method in async:
@Slf4j
@Component
public class TestBean {
@Async
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("First bean print");
}
}
Output:
2018-09-05 13:28:07.868 INFO 8608 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:07.868 INFO 8608 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:08.860 INFO 8608 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:09.860 INFO 8608 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:10.860 INFO 8608 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
Every 1 sec start new Thread. That's it! But what if I return @Scheduled annotation:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();//async method
}
The result is the same as in the previous version. exactly what is needed!
But now I want use second bean. I Make method into second bean Async too and try start:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();
testBean2.test();
}
Output:
2018-09-05 13:32:46.079 INFO 11108 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:32:46.079 INFO 11108 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:32:47.074 INFO 11108 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:32:47.074 INFO 11108 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:32:48.074 INFO 11108 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
Both methods use ONE ThreadPoolTaskScheduler with 5 threads. But I need start each method with different ThreadPoolTaskScheduler. I create second ThreadPoolTaskScheduler:
@Bean
ThreadPoolTaskScheduler taskScheduler2(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(9);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
threadPoolTaskScheduler.setThreadNamePrefix("TASK_SCHEDULER_SECOND-");
return threadPoolTaskScheduler;
}
And start:
2018-09-05 13:35:31.152 INFO 14544 --- [ main] c.e.scheduling.SchedulingApplication : Started SchedulingApplication in 1.669 seconds (JVM running for 2.141)
2018-09-05 13:35:40.134 INFO 14544 --- [cTaskExecutor-2] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:35:40.134 INFO 14544 --- [cTaskExecutor-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:41.127 INFO 14544 --- [cTaskExecutor-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:35:41.127 INFO 14544 --- [cTaskExecutor-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:42.127 INFO 14544 --- [cTaskExecutor-5] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:42.127 INFO 14544 --- [cTaskExecutor-6] com.example.scheduling.TestBean2 : Second bean print
Both beans print log but with cTaskExecutor and not use tasckScheduler1 or tasckScheduler2
It is my first question - Why? How can it worked?
Now I tried use this implementation:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
private final ThreadPoolTaskScheduler poolTaskScheduler1;
private final ThreadPoolTaskScheduler poolTaskScheduler2;
public MyScheduler(TestBean testBean, TestBean2 testBean2,
@Qualifier("first") ThreadPoolTaskScheduler poolTaskScheduler1,
@Qualifier("second") ThreadPoolTaskScheduler poolTaskScheduler2) {
this.testBean = testBean;
this.testBean2 = testBean2;
this.poolTaskScheduler1 = poolTaskScheduler1;
this.poolTaskScheduler2 = poolTaskScheduler2;
}
// @Scheduled(fixedRate = 1000L)
@PostConstruct
public void test() {
poolTaskScheduler1.scheduleAtFixedRate(testBean::test, 1000L);
poolTaskScheduler2.scheduleAtFixedRate(testBean2::test, 1000L);
}
}
Output: nothing changed.
And In finished I revert code:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();
testBean2.test();
}
And use @Async with qualifier:
@Async("first")
@Async("second")
Output:
2018-09-05 13:44:11.489 INFO 7432 --- [EDULER_SECOND-1] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:11.489 INFO 7432 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:12.484 INFO 7432 --- [EDULER_SECOND-2] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:12.484 INFO 7432 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:13.484 INFO 7432 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:13.484 INFO 7432 --- [EDULER_SECOND-3] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:14.484 INFO 7432 --- [EDULER_SECOND-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:14.484 INFO 7432 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:15.484 INFO 7432 --- [EDULER_SECOND-5] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:15.484 INFO 7432 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:16.483 INFO 7432 --- [EDULER_SECOND-6] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:17.483 INFO 7432 --- [EDULER_SECOND-7] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:18.483 INFO 7432 --- [EDULER_SECOND-8] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:19.483 INFO 7432 --- [EDULER_SECOND-9] com.example.scheduling.TestBean2 : Second bean print
exactly what is needed! But I do not understand whether I'm doing the right thing
If I change ThreadPoolTaskScheduler to ThreadPoolTaskExecutor everything works the same way. So what should I use?
ThreadPoolTaskScheduler or ThreadPoolTaskExecutor
@Scheduled or ThreadPoolTaskScheduler/ThreadPoolTaskExecutor from code?
@Scheduled with ThreadPoolTaskScheduler/ThreadPoolTaskExecutor from code or @Async?