I've the problem, that I want to create a scheduled task during runtime. The scheduled task should be triggered with a fixed rate. But now I'm having the problem that the manual setup schedules are not triggered in an async way.
The main problem is, that we do not have any fix point were we can start the scheduler. It should get created when I read a specific value (1) and gets destroyed when the value changes back (0). Otherwise we could use the annotation configuration described in test 1 below.
What I have tried so far:
1. Schedule with @Scheduled(fixedRate = 500L)
and @Async
Code
@Async
@Scheduled(fixedRate = 500L)
public void annotationTest() {
UUID id = UUID.randomUUID();
log.warn("Hello from Thread {} going to sleep", id);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.warn("Finished Thread {}", id);
}
Also having the @EnableAsync
and @EnableScheduling
annotations on class level.
Result
09:56:24.855 [task-5] : Hello from Thread 3b5514b2-3b80-4641-bf12-2cd320c4b6e5 going to sleep
09:56:25.355 [task-6] : Hello from Thread e98514a7-e193-422b-9569-f7635deb33f8 going to sleep
09:56:25.356 [task-4] : Finished Thread d86f5f24-bffb-4ddd-93fe-2334ed48cf91
09:56:25.854 [task-7] : Hello from Thread cfc2ab03-4e7e-4a4a-aa08-41d696cb6df7 going to sleep
09:56:25.855 [task-5] : Finished Thread 3b5514b2-3b80-4641-bf12-2cd320c4b6e5
09:56:26.355 [task-6] : Finished Thread e98514a7-e193-422b-9569-f7635deb33f8
Comment
This works as expected, but we are not able to use it, because we have to create the scheduler during runtime and destroy it after a specific time/input.
2. Setting up a ScheduledTaskRegistrar
Code
//@Configuration
@Bean
public ScheduledTaskRegistrar scheduledTaskRegistrar() {
ScheduledTaskRegistrar scheduledTaskRegistrar = new ScheduledTaskRegistrar();
scheduledTaskRegistrar.setScheduler(threadPoolTaskScheduler());
return scheduledTaskRegistrar;
}
@Bean
public TaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20);
return scheduler;
}
//@Component
public void printMessages() {
scheduledTaskRegistrar.scheduleFixedRateTask(new FixedRateTask(new OwnRunnable(), 500L, 0L));
}
The OwnRunnable
will also sleep 1 second and print the finish Text afterwards
Result
10:13:56.983 [TaskScheduler-1] : Finished Thread 73f70de9-35d9-47f0-801b-fb2857ab1c34
10:13:56.984 [TaskScheduler-3] : Hello from Thread 7ab16380-8dba-49e1-bf0d-de8235f81195 going to sleep
10:13:57.984 [TaskScheduler-3] : Finished Thread 7ab16380-8dba-49e1-bf0d-de8235f81195
10:13:57.984 [TaskScheduler-2] : Hello from Thread cc152d2e-f93b-4770-ac55-853a4dd6be97 going to sleep
10:13:58.985 [TaskScheduler-2] : Finished Thread cc152d2e-f93b-4770-ac55-853a4dd6be97
10:13:58.985 [TaskScheduler-4] : Hello from Thread 8d4510a4-773d-49f3-b51b-e58e425b0b68 going to sleep
Comment
As we can see the tasks run in a synchronous way and will not fit to our requirement.
3. Other tests
All other tests are similar to the test described in 2 but will use some other configurations of the ScheduledTaskRegistrar
. The results are the same as in test 2.
ConcurrentTaskScheduler
instead of ThreadPoolTaskScheduler
ConcurrentTaskScheduler
with SimpleAsyncTaskExecutor
as ConcurrentExecutor
ConcurrentTaskScheduler
with ThreadPoolTaskExecutor
as ConcurrentExecutor
Question(s)
How can I use the configuration described in test 2 but get the result of test 1? Is there a way to use the @Async
annotation with solution described in test 2? Or does anyone have a better/ another solution for my problem?
Yes, it is possible. Assume that your class that implemented SchedulingConfigurer has a method, doMyJob(). You can annotate that method with Async and use the reference in FixedRateTask. Also notice the class level annotation
@Configuration
@EnableAsync
public class MyJobConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.scheduleFixedRateTask(new FixedRateTask(this::doMyJob, 500L, 0L));
}
@Async
public void doMyJob() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Hope it helps
EDIT
I provided the code without testing. Recently when I tried to recreate this scenario, I noticed that if doMyJob is within SchedulingConfigurer, it will not be truly async (if delay is 5seconds and job takes 10seconds, next job runs only after 10seconds). But moving the method to a service class helped.
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