It is possible to define a Scheduled task in SpringBoot with the @scheduled annotation, but sometimes you will find that a Scheduled task is not executed when it is due. This is not always the case.

The following code defines a timed task that executes every ten seconds:

@Component
public class ScheduledTaskDemo {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskDemo.class);

    @Scheduled(cron = "0/10 * * * * *")
    public void execute(a) {
        logger.info("Scheduled task is running... ..."); }}Copy the code

When you start the SpringBoot application, you can see on the console that the scheduled task prints a log every 10 seconds

The 2017-12-21 22:20:20. 7424-832 the INFO [main] S.B.C.E.T.T omcatEmbeddedServletContainer: Tomcat started on the port (s) : 8000 (HTTP) 22:20:20 2017-12-21. 7424-859 the INFO [main] com. Example. Demo. DemoApplication: Started DemoApplication in 12.134 seconds (JVM running for 14.156) 2017-12-21 22:20:30.002 INFO 7424 — [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : Scheduled task is running… . The 2017-12-21 22:20:40. 7424-001 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: Scheduled task is running… . The 2017-12-21 22:20:50. 7424-002 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: Scheduled task is running… .

But it’s not over yet.

If no related log is displayed, check to see if @enablesCheduling annotations are added to the entry class or Configuration class

In the related code above, we used the cron expression to specify the execution point of the scheduled task, i.e., every 10 seconds starting at 0 seconds, and now we add another scheduled task:

@Component
public class SecondScheduledTaskDemo {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskDemo.class);

    @Scheduled(cron = "0/10 * * * * *")
    public void second(a) {
        logger.info("Second scheduled task is starting... ...");
        logger.info("Second scheduled task is ending... ..."); }}Copy the code

Now start the SpringBoot application again and look at log:

The 2017-12-21 23:12:20. 13208-007 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is starting… . The 2017-12-21 23:12:35. 13208-013 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is ending… . The 2017-12-21 23:12:35. 13208-016 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. SecondScheduledTaskDemo: Second scheduled task is starting… . The 2017-12-21 23:12:35. 13208-016 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. SecondScheduledTaskDemo: Second scheduled task is ending… . The 2017-12-21 23:12:40. 13208-000 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is starting… . The 2017-12-21 23:12:55. 13208-001 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is ending… . The 2017-12-21 23:12:55. 13208-002 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. SecondScheduledTaskDemo: Second scheduled task is starting… . The 2017-12-21 23:12:55. 13208-002 the INFO [] – thread pool – 1-1 C.E.D emo. Scheduled. SecondScheduledTaskDemo: Second scheduled task is ending… .

Note the execution time of the scheduled task in the log. The second scheduled task should be executed every 10 seconds, but from 23:12:20 to 23:13:55, the scheduled task should be executed four times, but only two times.

Is the cron expression wrong?

No.

To find out why, start with the @Scheduled annotated source code:

 *
 * <p>Processing of {@code @Scheduled} annotations is performed by
 * registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
 * done manually or, more conveniently, through the {@code <task:annotation-driven/>}
 * element or @{@link EnableScheduling} annotation.
 *
Copy the code

To highlight, every @ Scheduled annotation methods will be registered as a ScheduledAnnotationBeanPostProcessor, then look down ScheduledAnnotationBeanPostProcessor:

	/**
	 * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
	 * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
	 * to be wrapped as a TaskScheduler.
	 * <p>If not specified, default scheduler resolution will apply: searching for a
	 * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}
	 * bean named "taskScheduler" otherwise; the same lookup will also be performed for
	 * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,
	 * a local single-threaded default scheduler will be created within the registrar.
	 * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME
	 */
	public void setScheduler(Object scheduler) {
		this.scheduler = scheduler;
	}
Copy the code

Here’s the thing. Pay attention to this sentence:

If neither of the two is resolvable, a local single-threaded default scheduler will be created within the registrar.

This means that if we don’t actively configure the TaskScheduler we need, SpringBoot will default to using a single-threaded scheduler to handle the Scheduled tasks we implemented with the @scheduled annotation, which makes sense:

23:12:20: The first scheduled task is executed on thread pool1-thread-1. There is only one thread in pool pool 1 because the scheduler is not configured. The thread starts to sleep after printing the starting log. The second scheduled task is also ready to execute, but there are no more threads in the thread pool and you have to wait.

23:12:30, the first scheduled task is still sleeping, and the second scheduled task is still waiting.

23:12:35, the first scheduled task sleep ends, printing the ending log and ending, at this time the thread pool is free, the second scheduled task starts to execute from the wait state directly, after the execution, the thread pool is free.

23:12:40, the thread pool is idle, the first scheduled task is executed, the starting log is printed, and the sleep starts.

Note that it is also possible to perform a second timed task, depending on the CPU.

. .

Once you understand the process, it’s easy to solve the problem.

As described in the previous comment, all we need to do is provide a TaskScheduler that meets our needs and register it with the context.

@Configuration
public class ScheduledTaskConfiguration implements SchedulingConfigurer {

    /**
     * Callback allowing a {@link TaskScheduler
     * TaskScheduler} and specific {@link Task Task}
     * instances to be registered against the given the {@link ScheduledTaskRegistrar}
     *
     * @param taskRegistrar the registrar to be configured.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(2); taskScheduler.initialize(); taskRegistrar.setTaskScheduler(taskScheduler); }}Copy the code

The above code provides a taskScheduler with a thread pool size of 2. Now start SpringBoot to see the effect.

The 23:28:30 2017-12-21. 7972-001 the INFO [TaskScheduler – 1] C.E.D emo. Scheduled. ScheduledTaskDemo: Second scheduled task is starting… . The 2017-12-21 23:28:30. 7972-001 the INFO [TaskScheduler – 2] C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is starting… . The 23:28:30 2017-12-21. 7972-002 the INFO [TaskScheduler – 1] C.E.D emo. Scheduled. ScheduledTaskDemo: Second scheduled task is ending… . The 23:28:40 2017-12-21. 7972-001 the INFO [TaskScheduler – 1] C.E.D emo. Scheduled. ScheduledTaskDemo: Second scheduled task is starting… . The 23:28:40 2017-12-21. 7972-001 the INFO [TaskScheduler – 1] C.E.D emo. Scheduled. ScheduledTaskDemo: Second scheduled task is ending… . The 2017-12-21 23:28:45. 7972-002 the INFO [TaskScheduler – 2] C.E.D emo. Scheduled. ScheduledTaskDemo: First scheduled task is ending… .

As you can see, when there are two threads in the thread pool, the two scheduled tasks are fired at a predetermined time without affecting each other.