3 способа реализации задач синхронизации в Spring Boot

Spring Boot

Ref

Выполнение задач на время

  • Способ 1: на основеjava.util.TimerТаймер для выполнения задач синхронизации, подобных будильникам.
  • Способ 2. Используйте сторонние платформы запланированных задач с открытым исходным кодом, такие как Quartz, elastic-job, xxl-job и т. д., которые подходят для приложений распределенных проектов. Недостатком этого метода является сложность настройки.
  • Способ 3: используйте аннотацию, предоставленную Spring@Schedule, разработка проста, использование более удобно.

java.util.Timer реализует временные задачи

на основеjava.util.TimerТаймер реализует временные задачи, аналогичные будильнику. Этот метод редко используется в проекте, см. следующую демонстрацию.

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class SpringbootAppApplication {

	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		SpringApplication.run(SpringbootAppApplication.class, args);
		System.out.println("Server is running ...");

		TimerTask timerTask =  new TimerTask() {
			@Override
			public void run() {
				System.out.println("task  run:"+ new Date());
			}
		};
		Timer timer = new Timer();
		timer.schedule(timerTask,10,3000);
	}
}

ScheduledExecutorService реализует временные задачи

Метод похож наTimer, см. следующую демонстрацию.

public class TestScheduledExecutorService {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

        /**
		 *  @param command the task to execute 任务体
		 *  @param initialDelay the time to delay first execution 首次执行的延时时间
		 *  @param period the period between successive executions 任务执行间隔
		 *  @param unit the time unit of the initialDelay and period parameters 间隔时间单位
		 */
		service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
        service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
    }
}

@Schedule реализует временные задачи

Demo

  1. Во-первых, добавьте класс запуска проекта@EnableSchedulingАннотация, чтобы включить поддержку задач на время.@EnableSchedulingРоль аннотаций заключается в обнаружении аннотаций@Scheduledзадачу и выполнить ее в фоновом режиме.
@SpringBootApplication
@EnableScheduling
public class ScheduledApplication {

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

}
  1. Напишите классы и методы задач по времени. Классы задач по времени загружаются через Spring IOC с использованием@Componentаннотация
  2. Используйте метод синхронизации@Scheduledаннотация. В следующем кодеfixedRateдаlongТип, указывающий количество миллисекунд между выполнением задачи, рассчитанная по времени задача в следующем коде выполняется каждые 3 секунды.
@Component
public class ScheduledTask {

    @Scheduled(fixedRate = 3000)
    public void scheduledTask() {
        System.out.println("任务执行时间:" + LocalDateTime.now());
    }

}
  1. Запустите проект, журналы запуска и работы проекта выглядят следующим образом, вы можете видеть, что запись журнала выполнения печатается каждые 3 секунды.
Server is running ...
任务执行时间-ScheduledTask:2020-06-23T18:02:14.747
任务执行时间-ScheduledTask:2020-06-23T18:02:17.748
任务执行时间-ScheduledTask:2020-06-23T18:02:20.746
任务执行时间-ScheduledTask:2020-06-23T18:02:23.747

@Запланированная аннотация

В приведенной выше демонстрации используйте@Scheduled(fixedRate = 3000)Аннотация для определения задачи, которая будет выполняться каждые 3 секунды. за@ScheduledИспользование можно резюмировать следующим образом.

  • @Scheduled(fixedRate = 3000): Выполнить снова через 3 секунды после последней точки времени выполнения запуска (fixedRateАтрибут: задержка повторного выполнения запланированной задачи после запуска запланированной задачи (необходимо дождаться завершения последней запланированной задачи) в миллисекундах.
  • @Scheduled(fixedDelay = 3000): выполнить снова через 3 секунды после последней точки времени выполнения (fixedDelayАтрибут: задержка повторного выполнения запланированной задачи после выполнения запланированной задачи (необходимо дождаться завершения последней запланированной задачи), в миллисекундах)
  • @Scheduled(initialDelay = 1000, fixedRate = 3000): выполнить после первой задержки в 1 секунду, затем нажатьfixedRateПравила выполняются каждые 3 секунды (initialDelayАтрибут: время задержки первого выполнения запланированной задачи, которое необходимо согласовать.fixedDelayилиfixedRateиспользовать)
  • @Scheduled(cron="0 0 2 1 * ? *"):пройти черезcronПравила определения выражений

Многопоточное выполнение запланированных задач

Создайте несколько запланированных задач и напечатайте имя потока Пример кода выглядит следующим образом.

import org.slf4j.LoggerFactory;

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

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(){
        logger.info("使用cron---任务执行时间:{}  线程名称:{}",LocalDateTime.now(),Thread.currentThread().getName());
    }
    
    @Scheduled(fixedRate = 5000)
    public void scheduled1() {
        logger.info("fixedRate---任务执行时间:{}  线程名称:{}",LocalDateTime.now(),Thread.currentThread().getName());
    }
    @Scheduled(fixedDelay = 5000)
    public void scheduled2() {
        logger.info("fixedDelay---任务执行时间:{}  线程名称:{}",LocalDateTime.now(),Thread.currentThread().getName());
    }
}

Вывод программы следующий.

2020-06-23 23:31:04.447  INFO 34069 : fixedRate---任务执行时间:2020-06-23T23:31:04.447  线程名称:scheduling-1
2020-06-23 23:31:04.494  INFO 34069 : fixedDelay---任务执行时间:2020-06-23T23:31:04.494  线程名称:scheduling-1
2020-06-23 23:31:05.004  INFO 34069 : 使用cron---任务执行时间:2020-06-23T23:31:05.004  线程名称:scheduling-1
2020-06-23 23:31:09.445  INFO 34069 : fixedRate---任务执行时间:2020-06-23T23:31:09.445  线程名称:scheduling-1
2020-06-23 23:31:09.498  INFO 34069 : fixedDelay---任务执行时间:2020-06-23T23:31:09.498  线程名称:scheduling-1
2020-06-23 23:31:10.003  INFO 34069 : 使用cron---任务执行时间:2020-06-23T23:31:10.003  线程名称:scheduling-1
2020-06-23 23:31:14.444  INFO 34069 : fixedRate---任务执行时间:2020-06-23T23:31:14.444  线程名称:scheduling-1
2020-06-23 23:31:14.503  INFO 34069 : fixedDelay---任务执行时间:2020-06-23T23:31:14.503  线程名称:scheduling-1
2020-06-23 23:31:15.002  INFO 34069 : 使用cron---任务执行时间:2020-06-23T23:31:15.002  线程名称:scheduling-1

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

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

  1. Создайте класс конфигурации: в традиционном проекте Spring мы можем добавить конфигурацию задачи в файл конфигурации xml, тогда как в проектах Spring Boot мы обычно используем класс конфигурации конфигурации для добавления конфигурации, поэтому создайте новыйAsyncConfigсвоего рода.В классе конфигурации используйте@EnableAsyncАннотация включает поддержку асинхронных событий.
package com.lbs0912.spring.demo.app;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync

public class AsyncConfig {

    private int corePoolSize = 10;
    private int maxPoolSize = 200;
    private int queueCapacity = 10;

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.initialize();
        return executor;
    }
}
  • @Configuration: указывает, что класс является классом конфигурации.
  • @EnableAsync: включить поддержку асинхронных событий.
  1. Добавить в класс или метод запланированной задачи@AsyncАннотация, указывающая, что это временная задача для асинхронного события.
@Component
@Async
public class ScheduledTask {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
    

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(){
        logger.info("使用cron  线程名称:{}",Thread.currentThread().getName());
    }
    
    @Scheduled(fixedRate = 5000)
    public void scheduled1() {
        logger.info("fixedRate--- 线程名称:{}",Thread.currentThread().getName());
    }
    @Scheduled(fixedDelay = 5000)
    public void scheduled2() {
        logger.info("fixedDelay  线程名称:{}",Thread.currentThread().getName());
    }
}
  1. Запустите программу, вывод консоли выглядит следующим образом, вы можете видеть, что заданная по времени задача выполняется в несколько потоков.
2020-06-23 23:45:08.514  INFO 34824 : fixedRate--- 线程名称:taskExecutor-1
2020-06-23 23:45:08.514  INFO 34824 : fixedDelay  线程名称:taskExecutor-2
2020-06-23 23:45:10.005  INFO 34824 : 使用cron  线程名称:taskExecutor-3

2020-06-23 23:45:13.506  INFO 34824 : fixedRate--- 线程名称:taskExecutor-4
2020-06-23 23:45:13.510  INFO 34824 : fixedDelay  线程名称:taskExecutor-5
2020-06-23 23:45:15.005  INFO 34824 : 使用cron  线程名称:taskExecutor-6

2020-06-23 23:45:18.509  INFO 34824 : fixedRate--- 线程名称:taskExecutor-7
2020-06-23 23:45:18.511  INFO 34824 : fixedDelay  线程名称:taskExecutor-8
2020-06-23 23:45:20.005  INFO 34824 : 使用cron  线程名称:taskExecutor-9

2020-06-23 23:45:23.509  INFO 34824 : fixedRate--- 线程名称:taskExecutor-10
2020-06-23 23:45:23.511  INFO 34824 : fixedDelay  线程名称:taskExecutor-1
2020-06-23 23:45:25.005  INFO 34824 : 使用cron  线程名称:taskExecutor-2

2020-06-23 23:45:28.509  INFO 34824 : fixedRate--- 线程名称:taskExecutor-3
2020-06-23 23:45:28.512  INFO 34824 : fixedDelay  线程名称:taskExecutor-4
2020-06-23 23:45:30.005  INFO 34824 : 使用cron  线程名称:taskExecutor-5

2020-06-23 23:45:33.509  INFO 34824 : fixedRate--- 线程名称:taskExecutor-6
2020-06-23 23:45:33.513  INFO 34824 : fixedDelay  线程名称:taskExecutor-7
2020-06-23 23:45:35.005  INFO 34824 : 使用cron  线程名称:taskExecutor-8

...

Quartz реализует временные задачи

Basic

QuartzЭто проект с открытым исходным кодом, полностью разработанный на Java, который можно использовать для выполнения временных задач, подобныхjava.util.Timer. Но по сравнению сTimer,QuartzДобавлено много функций

  • Постоянные задания - т.е. сохраняют состояние запланированного времени
  • Управление заданиями - эффективное управление запланированными заданиями

Кварц можно разделить на 3 основные части, как показано на следующем рисунке.

  1. Задача:JobDetail, конкретная задача на время
  2. курок:Trigger,включаютSimpleTriggerиCronTrigger. Триггеры отвечают за запуск запланированных задач, и их основная функция заключается в указании времени выполнения, интервала выполнения и времени выполнения задания.
  3. планировщик:Scheduler, для планирования и как указать триггеры для выполнения определенных задач.

Demo-1

  1. добавить зависимости

Добавить кspring-boot-starter-quartzполагаться.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

Для версий ниже SpringBoot 1.5.9 вам также необходимо добавить следующие зависимости.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
</dependency>
  1. Создать запланированный класс задачJobQuartz, класс наследуетQuartzJobBean, и переписатьexecuteInternalметод.
package com.lbs0912.spring.demo.app.quartz;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;

/**
 * @author lbs
 */
public class JobQuartz extends QuartzJobBean {

    /**
     * 执行定时任务
     * @param jobExecutionContext  jobExecutionContext
     * @throws JobExecutionException JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("quartz task: " +  new Date());
    }
}
  1. Создать класс конфигурацииQuartzConfig
package com.lbs0912.spring.demo.app.quartz;


import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author lbs
 */
@Configuration
public class QuartzConfig {
    @Bean
    //创建JobDetail实例
    public JobDetail testJobDetail(){
        return JobBuilder.newJob(JobQuartz.class).withIdentity("jobQuartz").storeDurably().build();
    }
    @Bean
    public Trigger testTrigger(){
     
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(10)  //设置时间周期单位秒
                .repeatForever();
        //构建Trigger实例,每隔10s执行一次
        return TriggerBuilder.newTrigger().forJob(testJobDetail())
                .withIdentity("jobQuartz")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

  1. Запустите проект и просмотрите вывод журнала.
Server is running ...
quartz task: Wed Jun 24 00:49:07 CST 2020
quartz task: Wed Jun 24 00:49:17 CST 2020
quartz task: Wed Jun 24 00:49:27 CST 2020

Demo-2

  1. добавить зависимости
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  1. Создать задачуPrintWordsJobкласс, реализацияJobинтерфейс, переопределениеexecuteметод.
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

/**
 * @author liubaoshuai1
 */
public class PrintWordsJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));
    }
}
  1. Создать планировщикScheduleи создайте триггер в этом классеTriggerнапример, выполнить задание.
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

@SpringBootApplication
@EnableScheduling
public class SpringbootAppApplication {

    /**
     * main方法
     *
     * @param args
     */
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        System.out.println("Server is running ...");

        // 1、创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        // 2、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                .withIdentity("job1", "group1").build();

        // 3、构建Trigger实例,每隔1s执行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .startNow()//立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
						.withIntervalInSeconds(1)//每隔1s执行一次
                        .repeatForever()).build();//一直执行

        //4、执行
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();

        //睡眠
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println("--------scheduler shutdown ! ------------");

    }
}
  1. Запустив программу, вы увидите, что программа будет распечатывать содержимое каждые 1 с и завершать работу через одну минуту.
11:05:27.551 [DefaultQuartzScheduler_Worker-9] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
PrintWordsJob start at:20-06-24 11-05-27, prints: Hello Job-5

11:05:28.548 [DefaultQuartzScheduler_Worker-10] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
PrintWordsJob start at:20-06-24 11-05-28, prints: Hello Job-56

11:05:29.548 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
PrintWordsJob start at:20-06-24 11-05-29, prints: Hello Job-82

11:05:29.550 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
--------scheduler shutdown ! ------------

Анализ кварцевого кода

Далее описываются несколько параметров фреймворка Quartz в связи с проектом Demo-2.

  • Работа и JobDetail
  • JobExecutionContext
  • JobDataMap
  • Триггер, SimpleTrigger, CronTrigger

Job

Jobэто интерфейс в Quartz, только под интерфейсомexecuteметод, в котором написана бизнес-логика.

package org.quartz;

public interface Job {
    void execute(org.quartz.JobExecutionContext jobExecutionContext) throws org.quartz.JobExecutionException;
}

JobDetail

JobDetailИспользуется для привязки задания, предоставляя ряд свойств для экземпляра задания.

  • name
  • group
  • jobClass
  • jobDataMap

JobDetailПривязывать указанное задание каждый разSchedulerПри планировании и выполнении задания сначала будет получено соответствующее задание, затем будет создан экземпляр задания, а затем задание будет выполнено.execute()Содержание. После завершения выполнения задачи соответствующий экземпляр объекта Job будет освобожден и очищен сборщиком мусора JVM.

Почему он предназначен дляJobDetail + Job, не используйте напрямуюJob?

JobDetailОпределение — это данные задачи, а реальная логика выполнения — в задании. Это связано с тем, что задачи, скорее всего, будут выполняться одновременно, еслиSchedulerПри непосредственном использовании Job возникнет проблема одновременного доступа к одному и тому же экземпляру Job. иJobDetail + Jobметод, каждый раз, когда выполняется Sheduler, он будетJobDetailСоздайте новый экземпляр Job, чтобы обойти проблему одновременного доступа.

JobExecutionContext

JobExecutionContextСодержит среду выполнения Quartz и подробную информацию о самом задании. Когда Расписание планирует выполнение задания, оноJobExecutionContextперешел на эту работуexecute(), Работа может пройтиJobExecutionContextОбъект получает информацию.

JobExecutionContextПредоставленная информация выглядит следующим образом

Trigger

TriggerЭто триггер Quartz, который уведомит планировщик, когда следует выполнить соответствующее задание.

  • new Trigger().startAt(): Указывает время первого срабатывания триггера.
  • new Trigger().endAt(): указывает время окончания срабатывания триггера.

SimpleTriggerМожно выполнить рабочую задачу один раз в течение указанного периода времени или выполнить рабочую задачу несколько раз в течение периода времени.CronTriggerпланирование работы на основе календаря, в то время какSimpleTrigger- точный указанный интервал, поэтому по сравнению сSimpleTrigger,CroTriggerчаще используется.CroTriggerоснован наCronвыражение.

Cron-выражения

Ref

определение выражения

cronВыражение представляет собой строку, разделенную 6 пробелами на 7 полей, каждое поле представляет значение времени. Обычно часть, определяющая «год», может быть опущена, и обычно используемое выражение Cron состоит из первых 6 частей. Формат следующий

 [秒] [分] [时] [日] [月] [周] [年]
площадь Требуется ли значение и диапазон подстановочный знак
второй да 0-59 , - * /
Минута да 0-59 , - * /
Время да 0-23 , - * /
день да 1-31 , - * ? / L W
Луна да 1-12 или ЯНВАРЬ-ДЕКАБРЬ , - * /
неделю да 1-7 или вс-сб , - * ? / L #
год нет 1970-2099 , - * /

Следует отметить, что в выражении Cron «неделя» отсчитывается от воскресенья. в поле "неделя"1значит воскресенье,7Указывает на субботу.

Подстановочные знаки в Cron

  • ,: относится к выполнению в более чем двух точках времени. Если я определяю в поле «точки» как8,12,35, это означает, что задача синхронизации выполняется в 8-й, 12-й и 35-й точках соответственно.
  • -: указывает непрерывный диапазон в домене. Если указано в поле «время»1-6, значит срабатывает каждый час с 1 по 6, что эквивалентно1,2,3,4,5,6
  • *: Указывает все значения, которые можно интерпретировать как «каждое». Если установлено в поле «день»*, что указывает на то, что он срабатывает каждый день.
  • ?: Указывает, что значение не указано. Вариант использования заключается в том, что вам не нужно заботиться о значении текущей настройки этого поля. Например, чтобы запустить действие 8-го числа месяца, но не заботясь о дне недели, мы можем установить его так:0 0 0 8 * ?
  • /: Указывает шаг триггера (шаг),"/"Предыдущее значение представляет начальное значение ("*"эквивалент"0"), следующее значение представляет смещение, которое периодически запускается в определенном домене. например, определить "секунды"5/10Это означает, что он начинается с 5-й секунды и выполняется каждые 10 секунд, а на «минуте» означает, что он начинается с 5-й минуты и выполняется каждые 10 минут.
  • L: означает на английском языкеLASTозначает, можно использовать только в "день" и "неделя". В "День" он означает последний день текущего месяца (по текущему месяцу, если это февраль, он также будет основываться на том, является ли он годом), в "Неделе" означает субботу, т.е. эквивалентно «7» или «SAT». Если вы добавите число перед буквой «L», это будет означать последнее из данных. Например, установка формата «7L» для «week» означает «последняя суббота месяца».
  • W: Указывает, что он срабатывает в рабочий день (с понедельника по пятницу), ближайший к указанной дате. Его можно использовать только в «День» и только после определенного числа. Если для параметра «День» установлено «15W», это означает, что триггер срабатывает в рабочий день, ближайший к 15-му числу каждого месяца. Если 15-е число выпадает на субботу, найдите ближайшую пятницу (14-е число) для срабатывания; если 15-е число является рабочим днем, найдите ближайший следующий понедельник (16-е число) для срабатывания; если 15-е число приходится на рабочий день (понедельник на Еженедельно) 5), он будет активирован в этот день. Если это «1W», его можно переместить только на следующий ближайший рабочий день этого месяца и нельзя переместить на предыдущий месяц в течение нескольких месяцев.
  • #: Например,A#BУказывает ежемесячныйBнеделя неделиA(Недельный счет начинается с воскресенья), работает только на "неделю". Например2#3значит в 3-й вторник каждого месяца,5#3Представляет четверг 3-й недели месяца.

Уведомление,LИспользуется в поле «неделя», последний день недели — суббота. в поле "неделя"1значит воскресенье,7Обозначает субботу, т. е. еженедельный отсчет начинается с воскресенья.

инструмент визуализации

На указанном выше веб-сайте инструмента визуализации нажмите «Обратный анализ в пользовательский интерфейс», вы можете увидеть последние 5 раз выполнения запланированной задачи, что легко понять.

Также в IDEA установитеCron DescriptionПлагин также может отображать визуальную семантику.Как показано на рисунке ниже, наведите указатель мыши на выражение Cron, чтобы увидеть визуальную семантику.

Пример

Ниже приведены некоторые примеры, которые можно интерпретировать в свете приведенного выше объяснения.

  • Запускайте задачу в 12:00 каждую ночь:0 0 0 * * ?
  • Выполнять каждую 1 минуту:0 */1 * * * ?
  • Выполнять один раз в месяц в 1:00:0 0 1 1 * ?
  • Выполнять один раз в 23:00 последнего дня каждого месяца:0 0 23 L * ?
  • Каждую субботу в 3:00:0 0 3 ? * L
  • Выполнить один раз в 24 минуты 30 минут:0 24,30 * * * ?