CronTrigger запускает поток раньше указанного времени

У меня есть ThreadPoolTaskScheduler, запланированный CronTrigger, и я наблюдаю довольно странную вещь. Я запускаю в нем простую задачу, которая просто печатает текущую дату и время. Как это:

public class MyCron {
    Logger logger = LoggerFactory.getLogger(MyCron);

    private final ThreadPoolTaskScheduler scheduler;
    private final CronTrigger cronTrigger;
    private ScheduledFuture<?> runnableTask;

    public MyCron(String cronEntry) {
        this.scheduler = new ThreadPoolTaskScheduler();
        this.scheduler.setPoolSize(10);
        this.scheduler.setThreadNamePrefix("MyCron-" + name + "-");
        this.scheduler.initialize();
        this.cronTrigger = new CronTrigger(cronEntry);
        runnableTask = scheduler.schedule(this::workerThread, cronTrigger);
    }

где this::workerThread печатает дату и время. Но я заметил, что он запускается немного раньше запланированного, например, для строк cron 0 */5 * * * * и 0 */7 * * * * я наблюдаю следующее:

mycron  | 2024-05-30T17:59:59.844Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 17:59:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:04:59.785Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:04:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:06:59.702Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:06:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:09:59.785Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:09:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:13:59.702Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:13:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:14:59.784Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:14:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:19:59.785Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:19:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:20:59.665Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:20:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:24:59.746Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:24:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:27:59.692Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:27:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:29:59.783Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:29:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:34:59.694Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:34:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:34:59.783Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:34:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:39:59.782Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:39:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:41:59.694Z  INFO 1 --- [n-Cron test 2-7] c.e   : Cron test 2 worker is running: Thu May 30 2024 18:41:59 GMT+0000 (UTC)
mycron  | 2024-05-30T18:44:59.790Z  INFO 1 --- [n-Cron test 1-1] c.e   : Cron test 1 worker is running: Thu May 30 2024 18:44:59 GMT+0000 (UTC)

Я не понимаю, как это возможно и почему такое могло произойти. Я делаю что-то неправильно? Мои версии: Running with Spring Boot v3.3.0, Spring v6.1.8

Спасибо!

Обновление: я вижу закрытый запрос, потому что вопрос неясен. Я предполагал, что это довольно ясно, но я все равно попытаюсь объяснить это дальше. Поскольку я указал, что секунды CronTrigger установлены на ноль, я ожидаю, что мой планировщик будет работать через ноль секунд, а не на 59-й секунде. Это минимум. Другая сторона заключается в том, что если я настрою запуск каждые 5 минут, я ожидаю, что он запустится в 18:05:00, а не в 18:04:59. Вот ссылка на javadoc: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/support/CronExpression.html#parse(java.lang.String)

Обновление/объяснение – почему это важно: В моем task я запускаю sql-запрос с условием where, например where timestamp < current_timestamp() Поскольку он выполняется внутри контейнера, он выполняется очень быстро, и условие не срабатывает. Странно, неужели это такой редкий случай?

Как вы думаете, почему он запускается раньше?

Khanna111 30.05.2024 23:48

Почему? В основном потому, что я указал, чтобы он запускался каждые 5 и 7 минут.

BUKTOP 31.05.2024 16:14

Ваши выражения cron недействительны: «0 */7 * * * *». Выражение Cron не поддерживает день недели и день месяца одновременно. Вот допустимое выражение cron каждые 7 минут: «0 0/7 0 ? * * *»

AnotherOne 31.05.2024 16:18

@AnotherOne это не cron, как я уже писал, это CronTrigger docs.spring.io/spring-framework/docs/current/javadoc-api/org‌​/…

BUKTOP 31.05.2024 16:21

0 */5 * * * * ==> каждые 5 минут. Попробуйте 0 0/5 * * * *

Khanna111 01.06.2024 01:36

@Khanna111Khanna111 Извините, не понял вашей мысли. Не могли бы вы уточнить, пожалуйста?

BUKTOP 01.06.2024 02:39

@Khanna111 Типа, я имею в виду - сначала "0" означает "каждую 0-ю секунду". И что я наблюдаю - секунды, как вы могли видеть в журнале, равны 59

BUKTOP 01.06.2024 02:41
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
7
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Реализация

CronExpression

округляет до секунды. Поэтому он не может гарантировать выполнение именно в эту секунду (каждую 0-ю секунду).

Из журналов видно, что значение milli second округляется до ближайшей секунды. Таким образом, хотя в качестве второго значения отображается 59, мс округляются в большую сторону и «каждая 0-я секунда» является истиной.

Не могли бы вы поделиться источником этой информации?

BUKTOP 01.06.2024 03:25
Ответ принят как подходящий

Ну, судя по выпуску 29735, они считают, что это не баг. Итак, я добавил такой код в task в качестве обходного пути.

long now = System.currentTimeMillis();
long fraction = now - now / 1000 * 1000;
if (fraction > 500) {
    TimeUnit.MILLISECONDS.sleep(1000L - fraction);
}

Немного слишком некрасиво и вонюче, но других решений я не вижу, так как исправлять это не хотят.

Другие вопросы по теме