[SpringBoot] Почему моя запланированная задача не выполняется?

Spring Boot

В SpringBoot вы можете пройти@ScheduledАннотируйте, чтобы определить временную задачу, но иногда вы можете обнаружить, что некоторые временные задачи не выполняются, когда время истекло, но они не выполняются каждый раз. Что происходит?

Следующий код определяет временную задачу, которая выполняется каждые десять секунд:

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

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

В этот момент запустите приложение SpringBoot, и вы увидите в консоли, что это запланированное задание печатает журнал каждые 10 секунд.

2017-12-21 22:20:20.832 INFO 7424 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8000 (http) 2017-12-21 22:20:20.859 INFO 7424 --- [ 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... ... 2017-12-21 22:20:40.001 INFO 7424 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : Scheduled task is running... ... 2017-12-21 22:20:50.002 INFO 7424 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : Scheduled task is running... ...

Однако это еще не конец.

Если соответствующий журнал не отображается, проверьте, был ли добавлен класс записи или класс конфигурации.@EnableSchedulingаннотация

В соответствующем коде выше мы используем выражение cron для указания точки времени выполнения запланированной задачи, то есть начиная с 0 секунд и выполняя ее каждые 10 секунд, теперь мы добавляем еще одну запланированную задачу:

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

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

}

Теперь снова запустите приложение SpringBoot и посмотрите лог:

2017-12-21 23:12:20.007 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is starting... ... 2017-12-21 23:12:35.013 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is ending... ... 2017-12-21 23:12:35.016 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.SecondScheduledTaskDemo : Second scheduled task is starting... ... 2017-12-21 23:12:35.016 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.SecondScheduledTaskDemo : Second scheduled task is ending... ... 2017-12-21 23:12:40.000 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is starting... ... 2017-12-21 23:12:55.001 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is ending... ... 2017-12-21 23:12:55.002 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.SecondScheduledTaskDemo : Second scheduled task is starting... ... 2017-12-21 23:12:55.002 INFO 13208 --- [pool-1-thread-1] c.e.demo.scheduled.SecondScheduledTaskDemo : Second scheduled task is ending... ...

Обратите внимание на время выполнения запланированной задачи в журнале, вторая запланированная задача должна была выполняться каждые 10 секунд, но с 23:12:20 до 23:13:55 она должна была выполняться 4 раза, но казнены только 2. Второсортные.

Выражение cron неверно?

No.

Чтобы найти причину, начнем с@ScheduledИсходный код аннотации начинает находить:

 *
 * <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.
 *

внимание, у каждого есть@ScheduledАннотированные методы регистрируются какScheduledAnnotationBeanPostProcessor, а затем посмотрите вниз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;
	}

Вот в чем дело, обратите внимание на это предложение:

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

Это предложение означает, что если мы не будем активно настраивать то, что нам нужноTaskScheduler, SpringBoot по умолчанию будет использовать однопоточный планировщик для обработки нашего@ScheduledЗадачи синхронизации, реализуемые аннотациями, можно понять из вопроса, который мы только что задали:

23:12:20 начинается выполнение первой запланированной задачи в пуле потоков-1-потоке-1.Поскольку мы не настроили планировщик, в настоящее время в этом пуле потоков 1 есть только один поток.После печати стартового журнала это поток Начать спящий режим; вторая запланированная задача также готова к выполнению, но в пуле потоков больше нет потоков, поэтому она может только ждать.

23:12:30, первая запланированная задача все еще находится в спящем режиме, а вторая запланированная задача все еще ожидает.

23:12:35, первая задача по времени завершает сон, печатает журнал окончания и завершается. В это время пул потоков простаивает, а вторая задача по времени начинает выполняться непосредственно из состояния ожидания. После завершения выполнения поток бассейн бесплатный.

23:12:40, пул потоков простаивает, выполняется первое запланированное задание, печатается начальный журнал и начинается сон.

Обратите внимание, что здесь также можно выполнить вторую задачу по времени, в зависимости от ЦП.

... ...

Как только вы поймете процесс, решить эту проблему будет очень просто.

Согласно только что прокомментированному описанию, нам нужно предоставить только тот, который соответствует нашим потребностям.TaskSchedulerИ зарегистрируйте его в контексте.

@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);
    }
}

Приведенный выше код предоставляет планировщик задач с размером пула потоков, равным 2. Теперь запустите SpringBoot, чтобы увидеть эффект.

2017-12-21 23:28:30.001 INFO 7972 --- [TaskScheduler-1] c.e.demo.scheduled.ScheduledTaskDemo : Second scheduled task is starting... ... 2017-12-21 23:28:30.001 INFO 7972 --- [TaskScheduler-2] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is starting... ... 2017-12-21 23:28:30.002 INFO 7972 --- [TaskScheduler-1] c.e.demo.scheduled.ScheduledTaskDemo : Second scheduled task is ending... ... 2017-12-21 23:28:40.001 INFO 7972 --- [TaskScheduler-1] c.e.demo.scheduled.ScheduledTaskDemo : Second scheduled task is starting... ... 2017-12-21 23:28:40.001 INFO 7972 --- [TaskScheduler-1] c.e.demo.scheduled.ScheduledTaskDemo : Second scheduled task is ending... ... 2017-12-21 23:28:45.002 INFO 7972 --- [TaskScheduler-2] c.e.demo.scheduled.ScheduledTaskDemo : First scheduled task is ending... ...

Видно, что когда в пуле потоков есть два потока, две запланированные задачи запускаются в соответствии с заданным временем и не влияют друг на друга.