Часто нам требуется создать расписание для системы, чтобы она выполняла определённые задачи. Spring Boot уже предоставляет такую возможность, и нам нужно только использовать её. Конечно, вы можете не использовать встроенные расписания Spring Boot и вместо этого интегрировать Quartz, что также является хорошим выбором.
В данной статье не рассматривается интеграция Spring Boot с Quartz. Здесь демонстрируется только использование встроенных функций Spring Boot для создания расписаний. Ссылка на соответствующий код: https://github.com/Snailclimb/springboot-guide/tree/master/springboot-schedule-tast
Для этого нам достаточно иметь базовые зависимости Spring Boot, поэтому здесь не будет приведено конфигурационных файлов.
Мы можем легко создать расписание с помощью аннотации @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");
/**
* 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);
}
}
По умолчанию, задачи, помеченные аннотацией @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);
}
}
Добавление этого кода изменит имя текущего потока.
@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 )