1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/GiteeOS-springboot-guide

Клонировать/Скачать
SpringBoot-ScheduleTasks.md 12 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 04.06.2025 10:20 f496caa

Часто нам требуется создать расписание для системы, чтобы она выполняла определённые задачи. Spring Boot уже предоставляет такую возможность, и нам нужно только использовать её. Конечно, вы можете не использовать встроенные расписания Spring Boot и вместо этого интегрировать Quartz, что также является хорошим выбором.

В данной статье не рассматривается интеграция Spring Boot с Quartz. Здесь демонстрируется только использование встроенных функций Spring Boot для создания расписаний. Ссылка на соответствующий код: https://github.com/Snailclimb/springboot-guide/tree/master/springboot-schedule-tast

Использование Spring Schedule для создания расписаний

Для этого нам достаточно иметь базовые зависимости Spring Boot, поэтому здесь не будет приведено конфигурационных файлов.

1. Создание расписного задания

Мы можем легко создать расписание с помощью аннотации @Scheduled. Ниже приведены примеры использования @Scheduled, включая: выполнение с фиксированной скоростью, выполнение с фиксированным задержанием, выполнение с начальной задержкой и выполнение с использованием выражения Cron.

Выражение Cron: используется для определения времени или частоты выполнения задач в системах с расписанием. Оно позволяет вам задавать расписание для выполнения задач каждый день или каждый месяц и т.д.

Рекомендуется использовать онлайн-генератор выражений Cron: https://crontab.guru/```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit;

/**

  • @author shuang.kou */ @Component public class ScheduledTasks { private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    /**

    • fixedRate: выполнение с фиксированной скоростью. Выполняется каждые 5 секунд. */ @Scheduled(fixedRate = 5000) public void reportCurrentTimeWithFixedRate() { log.info("Current Thread : {}", Thread.currentThread().getName()); log.info("Fixed Rate Task : The time is now {}", dateFormat.format(new Date())); }
     * fixedDelay: фиксированная задержка выполнения. Задача будет выполнена через 2 секунды после успешного завершения предыдущего вызова.
     */
    @Scheduled(fixedDelay = 2000)
    public void reportCurrentTimeWithFixedDelay() {
        try {
            TimeUnit.SECONDS.sleep(3);
            log.info("Fixed Delay Task : The time is now {}", dateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }```markdown
    /**
     * initialDelay: начальная задержка. Первое выполнение задачи будет отложено на 5 секунд, а затем будет выполняться с постоянным интервалом в 5 секунд.
     */
    @Scheduled(initialDelay = 5000, fixedRate = 5000)
    public void reportCurrentTimeWithInitialDelay() {
        log.info("Задача с постоянным интервалом и начальной задержкой: время сейчас {}", dateFormat.format(new Date()));
    }

    /**
     * cron: использование выражения Cron. Задача будет выполняться каждую минуту в 1 и 2 секунды.
     */
    @Scheduled(cron = "1-2 * * * * ?")
    public void reportCurrentTimeWithCronExpression() {
        log.info("Выражение Cron: время сейчас {}", dateFormat.format(new Date()));
    }
}

В контексте fixedRate существует одна особенность, которая может вызвать проблемы. Предположим, что у нас есть задача, которая должна выполняться каждые 5 секунд. Эта задача состоит из четырёх подзадач, которые требуют времени на выполнение: 6 с, 6 с, 2 с и 3 с. Как будут выполняться эти задачи по умолчанию (в однопоточном режиме)?

Давайте напишем простой пример кода для проверки:

    private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    private List<Integer> index = Arrays.asList(6, 6, 2, 3);
    int i = 0;
    @Scheduled(fixedRate = 5000)
    public void reportCurrentTimeWithFixedRate() {
        if (i == 0) {
            log.info("Время начала: {}", dateFormat.format(new Date()));
        }
        if (i < 5) {
            try {
                TimeUnit.SECONDS.sleep(index.get(i));
                log.info("Задача с постоянным интервалом: время сейчас {}", dateFormat.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
    }

Выходные данные программы:

Время начала: 20:58:33
Задача с постоянным интервалом: время сейчас 20:58:39
Задача с постоянным интервалом: время сейчас 20:58:45
Задача с постоянным интервалом: время сейчас 20:58:47
Задача с постоянным интервалом: время сейчас 20:58:51

Схема выполнения задач должна помочь лучше понять ситуацию.

Если мы запустим этот метод в многопоточном режиме, результат будет совершенно другим. ### 2. Добавьте аннотацию `@EnableScheduling` к запускающему классу В Spring Boot для запуска планирования задач нам необходимо добавить аннотацию @EnableScheduling к запускающему классу.

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

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

3. Настройка собственного пула потоков для выполнения задач

По умолчанию, задачи, помеченные аннотацией @Scheduled, выполняются в пуле потоков размером 1, создаваемом Spring. Вы можете проверить это, добавив следующий код в метод, помеченный аннотацией @Scheduled.

logger.info("Текущий поток: {}", Thread.currentThread().getName());

Вы заметите, что добавление этого кода приведет к выводу имени потока scheduling-1 при каждом выполнении задачи.

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

@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
    private final int POOL_SIZE = 10;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

        threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
        threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
        threadPoolTaskScheduler.initialize();

        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}

Добавление этого кода изменит имя текущего потока.

4. Использование @EnableAsync и @Async для параллельного выполнения задачЕсли вы хотите выполнять задачи параллельно, вы можете использовать аннотации @EnableAsync и @Async.

@Component
@EnableAsync
public class AsyncScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    /**
     * fixedDelay: Задача выполняется с фиксированным интервалом задержки. Задача выполняется через 2 секунды после завершения предыдущей задачи.
     */
    //@Async
    @Scheduled(fixedDelay = 2000)
    public void reportCurrentTimeWithFixedDelay() {
        try {
            TimeUnit.SECONDS.sleep(3);
            log.info("Fixed Delay Task: Время выполнения {}", dateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Выполнение программы выводит следующее, метод reportCurrentTimeWithFixedDelay() выполняется каждые 5 секунд, так как все задачи, аннотированные с @Scheduled, выполняются в пуле потоков размером 1, созданном Spring.

Текущий поток: scheduling-1
Задача с фиксированным задержанием: Текущее время 14:24:23
Текущий поток: scheduling-1
Задача с фиксированным задержанием: Текущее время 14:24:28
Текущий поток: scheduling-1
Задача с фиксированным задержанием: Текущее время 14:24:33

После добавления аннотации @Async к методу reportCurrentTimeWithFixedDelay() вывод программы изменяется, и метод выполняется каждые 2 секунды.

Текущий поток: task-1
Задача с фиксированным задержанием: Текущее время 14:27:32
Текущий поток: task-2
Задача с фиксированным задержанием: Текущее время 14:27:34
Текущий поток: task-3
Задача с фиксированным задержанием: Текущее время 14:27:36
```Адрес кода: https://github.com/Snailclimb/springboot-guide/tree/master/source-code/advanced/springboot-schedule-task

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/GiteeOS-springboot-guide.git
git@api.gitlife.ru:oschina-mirror/GiteeOS-springboot-guide.git
oschina-mirror
GiteeOS-springboot-guide
GiteeOS-springboot-guide
master