定時任務應該這么玩


1.場景

在電商系統中會經常遇到這樣一種場景,就是商品的定時上下架功能,總不能每次都手動執行吧,這個時候我們首先想到的就是利用定時任務來實現這個功能。

目前實現定時任務主要有以下幾種方式:

  • JDK自帶 :JDK自帶的Timer以及JDK1.5+ 新增的ScheduledExecutorService;

  • 第三方框架 :使用 Quartz、elastic-job、xxl-job 等開源第三方定時任務框架,適合分布式項目應用。該方式的缺點是配置復雜。

  • Spring :使用 Spring 提供的一個注解 @Schedule,開發簡單,使用比較方便。

本文博主主要向大家介紹Quartz框架和Spring定時任務的使用。

2.什么是Quartz

Quartz 是一個完全由 Java 編寫的開源作業調度框架,為在 Java 應用程序中進行作業調度提供了簡單卻強大的機制。

Quartz 可以與 J2EE 與 J2SE 應用程序相結合也可以單獨使用。

Quartz 允許程序開發人員根據時間的間隔來調度作業。

Quartz 實現了作業和觸發器的多對多的關系,還能把多個作業與不同的觸發器關聯。

3.Quartz幾個核心概念

在正式學習使用Quartz之前,我們需要了解幾個有關Quartz的核心概念,方便我們后面學習

  1. Job 表示一個工作,要執行的具體內容。此接口中只有一個方法,如下:

    void execute(JobExecutionContext context) 
    // context是重要的上下文,可以訪問到關聯的JobDetail對象和本次觸發的Trigger對象,以及在此之上設定的數據。
    
  2. JobDetail 表示一個具體的可執行的調度程序,Job 是這個可執行程調度程序所要執行的內容,另外 JobDetail 還包含了這個任務調度的方案和策略。

  3. Trigger 代表一個調度參數的配置,什么時候去調。

  4. Scheduler 代表一個調度容器,一個調度容器中可以注冊多個 JobDetail 和 Trigger。當 Trigger 與 JobDetail 組合,就可以被 Scheduler 容器調度了。

4.Quartz初體驗

一、創建一個SpringBoot項目,pom.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.songguoliang</groupId>
    <artifactId>spring-boot-quartz</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>spring-boot-quartz</name>
    <description>Spring Boot使用Quartz定時任務</description>

    <!-- Spring Boot啟動器父類 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot web啟動器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- quartz -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

二、創建一個Job(Job里面是要執行的具體內容)

package com.example.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;

import java.time.LocalDateTime;

public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 通過context獲取trigger中的數據
        Object tv1 = context.getTrigger().getJobDataMap().get("t1");
        Object tv2 = context.getTrigger().getJobDataMap().get("t2");
        // 通過context獲取JobDetail中的數據
        Object jv1 = context.getJobDetail().getJobDataMap().get("j1");
        Object jv2 = context.getJobDetail().getJobDataMap().get("j2");
        Object sv = null;
        try {
            sv = context.getScheduler().getContext().get("skey");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        System.out.println(tv1+":"+tv2);
        System.out.println(jv1+":"+jv2);
        System.out.println(sv);
        System.out.println("date:"+ LocalDateTime.now());
    }
}

三、執行Job

package com.example.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzTest {
    public static void main(String[] args)  {
        try {
            //創建一個scheduler
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            //向scheduler中put值
            scheduler.getContext().put("skey", "svalue");

            //創建一個Trigger
            Trigger trigger = TriggerBuilder.newTrigger()
                    //給該Trigger起一個id
                    .withIdentity("trigger1")
                    //以Key-Value形式關聯數據
                    .usingJobData("t1", "tv1")
                    //每3秒觸發一次,無限循環
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
                            .repeatForever()).build();
            trigger.getJobDataMap().put("t2","tv2");

            //創建一個JobDetail
            JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                    //給該JobDetail起一個id
                    .withIdentity("myJob", "myGroup")
                    .usingJobData("j1", "jv1")
                    .build();
            jobDetail.getJobDataMap().put("j2", "jv2");

            //注冊trigger並啟動scheduler
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
            //如果想要停止這個Job,可以調用shutdown方法
            //scheduler.shutdown();

        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

控制台輸出

10:46:54.075 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
10:46:54.079 [main] INFO org.quartz.simpl.SimpleThreadPool - Job execution threads will use class loader of thread: main
10:46:54.089 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
10:46:54.089 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.2 created.
10:46:54.090 [main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.
10:46:54.091 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.2) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

10:46:54.091 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
10:46:54.091 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.2
10:46:54.104 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
10:46:54.104 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
10:46:54.106 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.example.quartz.TestJob
10:46:54.110 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
10:46:54.110 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
tv1:tv2
jv1:jv2
svalue
date:2020-12-19T10:46:54.144
10:46:57.092 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.example.quartz.TestJob
10:46:57.092 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
10:46:57.092 [DefaultQuartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
tv1:tv2
jv1:jv2
svalue
date:2020-12-19T10:46:57.092
10:47:00.101 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.example.quartz.TestJob
10:47:00.101 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
10:47:00.101 [DefaultQuartzScheduler_Worker-3] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
tv1:tv2
jv1:jv2
svalue
date:2020-12-19T10:47:00.101
10:47:03.096 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.example.quartz.TestJob
10:47:03.096 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
10:47:03.096 [DefaultQuartzScheduler_Worker-4] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
tv1:tv2
jv1:jv2
svalue
date:2020-12-19T10:47:03.096

從輸出結果我們可以看到此Job每隔3秒執行一次

有關概念

1、Job

job的一個 trigger 被觸發后(稍后會講到),execute() 方法會被 scheduler 的一個工作線程調用;傳遞給 execute() 方法的 JobExecutionContext 對象中保存着該 job 運行時的一些信息 ,執行 job 的 scheduler 的引用,觸發 job 的 trigger 的引用,JobDetail 對象引用,以及一些其它信息。

2、JobDetail

JobDetail 對象是在將 job 加入 scheduler 時,由客戶端程序(你的程序)創建的。它包含 job 的各種屬性設置,以及用於存儲 job 實例狀態信息的 JobDataMap

3、Trigger

Trigger 用於觸發 Job 的執行。當你准備調度一個 job 時,你創建一個 Trigger 的實例,然后設置調度相關的屬性。Trigger 也有一個相關聯的 JobDataMap,用於給 Job 傳遞一些觸發相關的參數。Quartz 自帶了各種不同類型的 Trigger,最常用的主要是 SimpleTrigger 和 CronTrigger。SimpleTrigger 主要用於一次性執行的 Job(只在某個特定的時間點執行一次),或者 Job 在特定的時間點執行,重復執行 N 次,每次執行間隔T個時間單位。CronTrigger 在基於日歷的調度上非常有用,如“每個星期五的正午”,或者“每月的第十天的上午 10:15”等。

5.JobDetail詳解

在定義一個Job時,我們需要實現Job接口,該接口只有一個execute方法。

從上一節的案例中我們可以發現,我們通過Scheduler去執行Job,我們傳給scheduler一個JobDetail實例,因為我們在創建JobDetail時,將要執行的job的類名傳給了JobDetail,所以scheduler就知道了要執行何種類型的job。(這里利用了Java中的反射創建實例對象)每次當scheduler執行job時,在調用其execute(…)方法之前會創建該類的一個新的實例;執行完畢,對該實例的引用就被丟棄了,實例會被垃圾回收;這種執行策略帶來的一個后果是,job必須有一個無參的構造函數(當使用默認的JobFactory時);另一個后果是,在job類中,不應該定義有狀態的數據屬性,因為在job的多次執行中,這些屬性的值不會保留。

那么我們該如何給Job配置相關屬性呢?答案就是通過JobDetail

JobDataMap

JobDataMap實現了Map接口,可以存放鍵值對數據,在Job執行的時候,我們就可以通過JobExecutionContext獲取到JobDataMap中的數據,如下

JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                    .withIdentity("myJob", "myGroup")
                    .usingJobData("j1", "jv1")
                    .usingJobData("j2","jv2")
                    .build();

在job的執行過程中,可以從JobDataMap中取出數據,如下示例:

Object jv1 = context.getJobDetail().getJobDataMap().get("j1");

當然,如果你希望實現屬性的自動注入,那么你可以使用下面的方法

package com.example.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzTest2 {
    public static void main(String[] args)  {
        try {

            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger1")
                    .usingJobData("t1", "tv1")
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
                            .repeatForever())
                    .build();

            JobDetail jobDetail = JobBuilder.newJob(TestJob2.class)
                    .withIdentity("jd")
                    .usingJobData("name", "張三")
                    .usingJobData("age", 12)
                    .build();

            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();

        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}
package com.example.quartz;

import org.quartz.*;

public class TestJob2 implements Job {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();

        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

        System.out.println("name:" + name + "age:" +age);
    }
}

給Job類加上get和set方法(屬性名稱要和JobDataMap中的key相同),那么JobDataMap中的值就是自動注入到Job中,不需要手動獲取

6.Triggers詳解

Trigger 用於觸發 Job 的執行。當你准備調度一個 job 時,你創建一個 Trigger 的實例,然后設置調度相關的屬性。所有類型的trigger都有TriggerKey這個屬性,表示trigger的身份;除此之外,trigger還有很多其它的公共屬性。這些屬性,在構建trigger的時候可以通過TriggerBuilder設置。

triggers公共屬性

  • jobKey屬性:當trigger觸發時被執行的job的身份;
  • startTime屬性:設置trigger第一次觸發的時間;該屬性的值是java.util.Date類型,表示某個指定的時間點;有些類型的trigger,會在設置的startTime時立即觸發,有些類型的trigger,表示其觸發是在startTime之后開始生效。比如,現在是1月份,你設置了一個trigger–“在每個月的第5天執行”,然后你將startTime屬性設置為4月1號,則該trigger第一次觸發會是在幾個月以后了(即4月5號)。
  • endTime屬性:表示trigger失效的時間點。比如,”每月第5天執行”的trigger,如果其endTime是7月1號,則其最后一次執行時間是6月5號。

優先級(priority)

如果你的trigger很多(或者Quartz線程池的工作線程太少),Quartz可能沒有足夠的資源同時觸發所有的trigger;這種情況下,你可能希望控制哪些trigger優先使用Quartz的工作線程,要達到該目的,可以在trigger上設置priority屬性。比如,你有N個trigger需要同時觸發,但只有Z個工作線程,優先級最高的Z個trigger會被首先觸發。如果沒有為trigger設置優先級,trigger使用默認優先級,值為5;priority屬性的值可以是任意整數,正數、負數都可以。

注意:只有同時觸發的trigger之間才會比較優先級。10:59觸發的trigger總是在11:00觸發的trigger之前執行。

注意:如果trigger是可恢復的,在恢復后再調度時,優先級與原trigger是一樣的。

錯過觸發(misfire Instructions)

trigger還有一個重要的屬性misfire;如果scheduler關閉了,或者Quartz線程池中沒有可用的線程來執行job,此時持久性的trigger就會錯過(miss)其觸發時間,即錯過觸發(misfire)。不同類型的trigger,有不同的misfire機制。它們默認都使用“智能機制(smart policy)”,即根據trigger的類型和配置動態調整行為

Simple Trigger

SimpleTrigger簡單點說,就是在具體的時間點執行一次,或者在具體的時間點執行,並且以指定的間隔重復執行若干次。類似於鬧鍾,你定了一個周末早晨7點的鬧鍾,這個鬧鍾會在周末早上7點准時響起。鬧鍾還有個功能就是過5分鍾之后再響一次,這對應着指定的間隔重復執行若干次。

1、指定時間開始觸發,不重復:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                    .withIdentity("st1", "group1")
                    .startAt(new Date()) // 從當前時間開始執行一次,不重復
                    .build();

2、指定時間觸發,每隔2秒執行一次,重復5次:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withIdentity("st2", "group1")
        .startAt(new Date())
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(2) // 2秒
                .withRepeatCount(5)// 5次
        )
        .build();

3、1分鍾以后開始觸發,僅執行一次:

long time = 1 * 60 * 1000;
Date now = new Date();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withIdentity("st3", "group1")
        .startAt(new Date(now.getTime() + time))
        .build();

4、立即觸發,每隔2秒鍾執行一次,直到2020-12-19 13:20:00

String dateStr="2020-12-19 13:20:00";
String pattern="yyyy-MM-dd HH:mm:ss";
SimpleDateFormat dateFormat=new SimpleDateFormat(pattern);
Date date = dateFormat.parse(dateStr);
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withIdentity("st4", "group1")
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInSeconds(2)
        .repeatForever())
        .endAt(date)
        .build();

5、在13:00觸發,然后每2小時重復一次:

String dateStr="2020-12-19 13:00:00";
String pattern="yyyy-MM-dd HH:mm:ss";
SimpleDateFormat dateFormat=new SimpleDateFormat(pattern);
Date date = dateFormat.parse(dateStr);
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withIdentity("st2", "group1")
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInHours(2)
        .repeatForever())
        .build();

SimpleTrigger Misfire

misfire:被錯過的執行任務策略

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withIdentity("st6")
        .withSchedule(
                SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInMinutes(5)
                        .repeatForever()
                        .withMisfireHandlingInstructionNextWithExistingCount()
        )
        .build();

CronTrigger

CronTrigger通常比Simple Trigger更有用,如果你需要在指定日期執行某項任務,使用CronTrigger就非常方便,比如如果你想在每月的15號給會員發放優惠券,或者每周五中午12點統計用戶本周使用產品時長。

cron 表達式是一個字符串,該字符串由 6 個空格分為 7 個域,每一個域代表一個時間含義。 通常定義 “年” 的部分可以省略,實際常用的 Cron 表達式由前 6 部分組成。格式如下

 [秒] [分] [時] [日] [月] [周] [年]
 Seconds  Minutes  Hours   Day-of-Month  Month   Day-of-Week	Year (optional field)
是否必填 值以及范圍 通配符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * ? / L W
1-12 或 JAN-DEC , - * /
1-7 或 SUN-SAT , - * ? / L #
1970-2099 , - * /

需要說明的是,Cron 表達式中,“周” 是從周日開始計算的。“周” 域上的 1 表示的是周日,7 表示周六。

每天晚上12點觸發任務:0 0 0 * * ?

每隔 1 分鍾執行一次:0 */1 * * * ?

每月 1 號凌晨 1 點執行一次:0 0 1 1 * ?

每月最后一天 23 點執行一次:0 0 23 L * ?

每周周六凌晨 3 點實行一次:0 0 3 ? * L

在24分,30分執行一次:0 24,30 * * * ?

是不是有點沒看懂,沒關系,我們可以使用Cron表達式生成器幫助我們生成Cron表達式

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 * * ?"))
        .build();

7.@Schedule實現定時任務

很多時候我們都需要為系統建立一個定時任務來幫我們做一些事情,SpringBoot 已經幫我們實現好了一個,我們只需要直接使用即可

一、引入依賴

<dependencies>

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

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

 <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
 </dependency>

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

</dependencies>

二、開啟注解

在 SpringBoot 中我們只需要在啟動類上加上@EnableScheduling便可以啟動定時任務了。

@SpringBootApplication
@EnableScheduling
public class TaskApplication {

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

三、創建scheduled task

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 wugongzi
 */
@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();
        }
    }

    /**
     * initialDelay:初始延遲。任務的第一次執行將延遲5秒,然后將以5秒的固定間隔執行。
     */
    @Scheduled(initialDelay = 5000, fixedRate = 5000)
    public void reportCurrentTimeWithInitialDelay() {
        log.info("Fixed Rate Task with Initial Delay : The time is now {}", dateFormat.format(new Date()));
    }

    /**
     * cron:使用Cron表達式。 每分鍾的1,2秒運行
     */
    @Scheduled(cron = "1-2 * * * * ? ")
    public void reportCurrentTimeWithCronExpression() {
        log.info("Cron Expression: The time is now {}", dateFormat.format(new Date()));
    }
}

啟動項目便可以看到效果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM