Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async scheduling with Spring ScheduledTaskRegistrar

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?

like image 282
Konrad Grüner Avatar asked Aug 19 '19 09:08

Konrad Grüner


1 Answers

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.

like image 125
Winster Avatar answered Oct 16 '22 22:10

Winster