定時任務應該這麼玩

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()));
    }
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章