Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @Scheduler parallel running

I have the following 3 classes:

ComponantA

package mytest.spring.test.spring;

import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ComponentA {

    Logger log = Logger.getLogger(ComponentB.class);

    @Scheduled(fixedRate=2000)
    public void sayHello() {
        for(int i=1 ; i<=5 ; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            log.info("Hello from ComponentA " + i);
        }
    }
}

ComponentB

package mytest.spring.test.spring;

import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ComponentB {

    Logger log = Logger.getLogger(ComponentB.class);

    @Scheduled(fixedRate=2000)
    public void sayHello() {
        for(int i=1 ; i<=3 ; i++) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            log.info("Hello from ComponentB " + i);
        }
    }
}

MyApplication

package mytest.spring.test.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

When I execute it, I'm getting the following output:

Hello from ComponentA 1
Hello from ComponentA 2
Hello from ComponentA 3
Hello from ComponentA 4
Hello from ComponentA 5
Hello from ComponentB 1
Hello from ComponentB 2
Hello from ComponentB 3
Hello from ComponentA 1
Hello from ComponentA 2
Hello from ComponentA 3
Hello from ComponentA 4
Hello from ComponentA 5
Hello from ComponentB 1
Hello from ComponentB 2
Hello from ComponentB 3
...

I need the 2 Scheduled methods to run in parallel, which is clearly not the cae according to the output I'm getting. I read that it should be possible to provide the @Schedule annotation with a custom TaskExecutor, with which it should be possible to define how many thread we want ...

Am I right ? I can't find how to provide this information.

like image 211
hublo Avatar asked May 20 '16 10:05

hublo


People also ask

How do I run multiple scheduler in Spring Boot?

We can easily schedule tasks in spring boot by using @Scheduled annotation. And then we need to enable scheduling by adding @EnableScheduling annotation to a spring configuration class. Spring uses ThreadPoolTaskScheduler for scheduled tasks, which internally delegates to a ScheduledExecutorService.

How can spring overlap schedules be prevented?

by default, spring uses a single-threaded Executor. so no two @Scheduled tasks will ever overlap. even two @Scheduled methods in completely unrelated classes will not overlap simply because there is only a single thread to execute all @Scheduled tasks.

How does spring ShedLock work?

ShedLock makes sure that your scheduled tasks are executed at most once at the same time. If a task is being executed on one node, it acquires a lock which prevents execution of the same task from another node (or thread).

What is ThreadPoolTaskScheduler?

ThreadPoolTaskScheduler is useful for internal thread management, as it delegates tasks to the ScheduledExecutorService, and implements the TaskExecutor interface. A single instance of it is able to handle asynchronous potential executions, as well as the @Scheduled annotation.


1 Answers

The documentation clearly states that:

By default, will be searching for an associated scheduler definition: either a unique TaskScheduler bean in the context, or a TaskScheduler bean named "taskScheduler" otherwise; the same lookup will also be performed for a ScheduledExecutorService bean. If neither of the two is resolvable, a local single-threaded default scheduler will be created and used within the registrar.

When more control is desired, a @Configuration class may implement SchedulingConfigurer. This allows access to the underlying ScheduledTaskRegistrar instance. For example, the following example demonstrates how to customize the Executor used to execute scheduled tasks:

@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }
}
like image 111
miensol Avatar answered Sep 26 '22 09:09

miensol