Quartz 使用:Job规哪、Trigger、Schedule

原文:https://blog.csdn.net/zixiao217/article/details/53044890

Job塌衰、Trigger诉稍、Schedule 使用

第一步:編寫一個(gè) job 類,需要實(shí)現(xiàn) org.quartz.Job 接口

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

public class HelloJob implements Job { 
    public void execute(final JobExecutionContext jobExecutionContext) throws JobExecutionException { 
        System.out.println(System.currentTimeMillis() + " - helloJob 任務(wù)執(zhí)行"); 
    } 
}

第二步:使用 job最疆、trigger杯巨、schedule 調(diào)用定時(shí)任務(wù)

import static org.quartz.JobBuilder.newJob; 
import static org.quartz.SimpleScheduleBuilder.simpleSchedule; 
import static org.quartz.TriggerBuilder.newTrigger; 
import org.quartz.JobDetail; 
import org.quartz.Scheduler; 
import org.quartz.SchedulerException; 
import org.quartz.Trigger; 
import org.quartz.impl.StdSchedulerFactory; 

public class QuartzTest { 
    public static void main(String[] args) { 
        try { 
            // 獲取一個(gè)調(diào)度程序的實(shí)例 
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); 
            System.out.println(scheduler.getSchedulerName() + " - " + scheduler.getSchedulerInstanceId()); 

            // 定義一個(gè) job,并綁定到 HelloJob.class 
            // 這里并不會(huì)馬上創(chuàng)建一個(gè) HelloJob 實(shí)例努酸,實(shí)例創(chuàng)建是在 scheduler 安排任務(wù)觸發(fā)執(zhí)行時(shí)創(chuàng)建的 
            JobDetail job = newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); 
            
            // 聲明一個(gè)觸發(fā)器 
            // schedule.start() 方法開始調(diào)用的時(shí)候執(zhí)行服爷,每間隔2秒執(zhí)行一次 
            Trigger trigger = newTrigger() 
                  .withIdentity("trigger1", "group1") 
                  .startNow() 
                  .withSchedule( 
                      simpleSchedule().withIntervalInSeconds(3).repeatForever() 
                  ).build(); 
          
            // 安排執(zhí)行任務(wù) 
            scheduler.scheduleJob(job, trigger); 

            // 啟動(dòng)任務(wù)調(diào)度程序(內(nèi)部機(jī)制是線程的啟動(dòng)) 
            scheduler.start(); 

            Thread.sleep(30000); 
            
            // 關(guān)閉任務(wù)調(diào)度程序 
            scheduler.shutdown(); 
        } catch (SchedulerException e) { 
            e.printStackTrace(); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    } 
}

第三步:執(zhí)行程序

DefaultQuartzScheduler - NON_CLUSTERED 
1560771694931 - helloJob 任務(wù)執(zhí)行 
1560771697879 - helloJob 任務(wù)執(zhí)行 
1560771700876 - helloJob 任務(wù)執(zhí)行 
1560771703880 - helloJob 任務(wù)執(zhí)行 
1560771706876 - helloJob 任務(wù)執(zhí)行 
1560771709881 - helloJob 任務(wù)執(zhí)行 
...

深入理解 Scheduler、Job、Trigger仍源、JobDetail

Scheduler 調(diào)度程序心褐,只有安排進(jìn)執(zhí)行計(jì)劃的 Job(通過 scheduler.scheduleJob 方法安排),當(dāng)生命的執(zhí)行時(shí)間(Trigger)到了的時(shí)候笼踩,該任務(wù)才會(huì)執(zhí)行逗爹。

Quartz API 幾個(gè)重要接口:

Scheduler,用于與調(diào)度程序交互的主程序接口戳表。
Job桶至,被調(diào)度程序執(zhí)行的任務(wù)類。
JobDetail匾旭,使用 JobDetail 來(lái)定義定時(shí)任務(wù)的實(shí)例镣屹。
Trigger,觸發(fā)器价涝,表明任務(wù)在什么時(shí)候會(huì)執(zhí)行女蜈。
JobBuilder,用于聲明一個(gè)任務(wù)實(shí)例色瘩,也可以定義關(guān)于該任務(wù)的詳情比如任務(wù)名伪窖、組名等。
TriggerBuilder居兆,觸發(fā)器創(chuàng)建器覆山,用于創(chuàng)建觸發(fā)器 trigger。

Scheduler 調(diào)度程序

org.quartz.Scheduler 是 Quartz 調(diào)度程序的主要接口泥栖。Scheduler 維護(hù)了一個(gè) JobDetails 和 Triggers 的注冊(cè)表簇宽。一旦在 Scheduler 注冊(cè)過了,當(dāng)定時(shí)任務(wù)觸發(fā)時(shí)間一到吧享,調(diào)度程序就會(huì)負(fù)責(zé)執(zhí)行預(yù)先定義的 Job魏割。

調(diào)度程序創(chuàng)建之后,處于“待機(jī)”狀態(tài)钢颂,必須調(diào)用 scheduler 的 start() 方法啟用調(diào)度程序钞它。可以使用 shutdown() 方法關(guān)閉調(diào)度程序殊鞭,使用 isShutdown() 方法判斷該調(diào)度程序是否已經(jīng)處于關(guān)閉狀態(tài)遭垛。通過 Scheduler.scheduleJob(…) 方法將任務(wù)納入調(diào)度程序中,當(dāng)任務(wù)觸發(fā)時(shí)間到了的時(shí)候钱豁,該任務(wù)將被執(zhí)行耻卡。

SchedulerFactory 調(diào)度程序工廠

調(diào)度程序 Scheduler 實(shí)例是通過 SchedulerFactory 工廠來(lái)創(chuàng)建的。SchedulerFactory 有兩個(gè)默認(rèn)的實(shí)現(xiàn)類:DirectSchedulerFactory 和 StdSchedulerFactory牲尺。

DirectSchedulerFactory

DirectSchedulerFactory 是一個(gè) org.quartz.SchedulerFactory 的單例實(shí)現(xiàn)卵酪。

示例1:使用 createVolatileScheduler 方法去創(chuàng)建一個(gè)不需要寫入數(shù)據(jù)庫(kù)的調(diào)度程序?qū)嵗?/p>

// 創(chuàng)建一個(gè)擁有10個(gè)線程的調(diào)度程序 
DirectSchedulerFactory.getInstance().createVolatileScheduler(10); 
DirectSchedulerFactory.getInstance().getScheduler().start();

示例2:使用 createScheduler 方法創(chuàng)建

public void createScheduler(String schedulerName, 
        String schedulerInstanceId, 
        ThreadPool threadPool, 
        JobStore jobStore, 
        String rmiRegistryHost, 
        int rmiRegistryPort)

// 創(chuàng)建線程池 
SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); 
threadPool.initialize(); 

// 創(chuàng)建 JobStore 
JobStore jobStore = new RAMJobStore(); 

// 創(chuàng)建調(diào)度程序 
DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); 

// 啟動(dòng)調(diào)度程序 
DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();

也可使用 JDBCJobStore:

DBConnectionManager.getInstance().addConnectionProvider("someDatasource", 
      new JNDIConnectionProvider("someDatasourceJNDIName")); 
JobStoreTX jdbcJobStore = new JobStoreTX(); 
jdbcJobStore.setDataSource("someDatasource"); 
jdbcJobStore.setPostgresStyleBlobs(true); 
jdbcJobStore.setTablePrefix("QRTZ_"); 
jdbcJobStore.setInstanceId("My Instance");

StdSchedulerFactory

StdSchedulerFactory 是 org.quartz.SchedulerFactory 的實(shí)現(xiàn)類幌蚊,基于 Quartz 屬性文件(quartz.properties)創(chuàng)建 Quartz Scheduler 調(diào)度程序的。

默認(rèn)情況下是加載當(dāng)前工作目錄下的 quartz.properties 文件溃卡。如果加載失敗溢豆,會(huì)去加載 org/quartz 包下的 quartz.properties 屬性文件∪诚郏可以在 org/quartz 包下找到其默認(rèn)的屬性文件的配置信息:

org.quartz.scheduler.instanceName: DefaultQuartzScheduler 
org.quartz.scheduler.rmi.export: false 
org.quartz.scheduler.rmi.proxy: false 
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false 
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool 
org.quartz.threadPool.threadCount: 10 
org.quartz.threadPool.threadPriority: 5 
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true 
org.quartz.jobStore.misfireThreshold: 60000 
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

如果不想使用默認(rèn)的文件名漩仙,可以指定 org.quartz.properties 屬性指向?qū)傩耘渲梦募犹赖;蛘呖梢栽谡{(diào)用 getScheduler() 方法之前調(diào)用 initialize(xxx)方法初始化工廠配置队他。

屬性配置文件中,還可以引用其他配置文件的信息峻村,可以使用 $@ 來(lái)引用:

quartz1.properties

org.quartz.scheduler.instanceName=HelloScheduler

quartz2.properties

org.quartz.scheduler.instanceName=$@org.quartz.scheduler.instanceName

參照以下 StdSchedulerFactory 的屬性配置麸折,實(shí)際使用中可以指定一些符合需求的參數(shù)。

PROPERTIES_FILE = "org.quartz.properties"; 
PROP_SCHED_INSTANCE_NAME = "org.quartz.scheduler.instanceName"; 
PROP_SCHED_INSTANCE_ID = "org.quartz.scheduler.instanceId"; 
PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS = "org.quartz.scheduler.instanceIdGenerator.class"; 
PROP_SCHED_THREAD_NAME = "org.quartz.scheduler.threadName"; 
PROP_SCHED_SKIP_UPDATE_CHECK = "org.quartz.scheduler.skipUpdateCheck"; 
PROP_SCHED_BATCH_TIME_WINDOW = "org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow"; 
PROP_SCHED_MAX_BATCH_SIZE = "org.quartz.scheduler.batchTriggerAcquisitionMaxCount"; 
PROP_SCHED_JMX_EXPORT = "org.quartz.scheduler.jmx.export"; 
PROP_SCHED_JMX_OBJECT_NAME = "org.quartz.scheduler.jmx.objectName"; 
PROP_SCHED_JMX_PROXY = "org.quartz.scheduler.jmx.proxy"; 
PROP_SCHED_JMX_PROXY_CLASS = "org.quartz.scheduler.jmx.proxy.class"; 
PROP_SCHED_RMI_EXPORT = "org.quartz.scheduler.rmi.export"; 
PROP_SCHED_RMI_PROXY = "org.quartz.scheduler.rmi.proxy"; 
PROP_SCHED_RMI_HOST = "org.quartz.scheduler.rmi.registryHost"; 
PROP_SCHED_RMI_PORT = "org.quartz.scheduler.rmi.registryPort"; 
PROP_SCHED_RMI_SERVER_PORT = "org.quartz.scheduler.rmi.serverPort"; 
PROP_SCHED_RMI_CREATE_REGISTRY = "org.quartz.scheduler.rmi.createRegistry"; 
PROP_SCHED_RMI_BIND_NAME = "org.quartz.scheduler.rmi.bindName"; 
PROP_SCHED_WRAP_JOB_IN_USER_TX = "org.quartz.scheduler.wrapJobExecutionInUserTransaction"; 
PROP_SCHED_USER_TX_URL = "org.quartz.scheduler.userTransactionURL"; 
PROP_SCHED_IDLE_WAIT_TIME = "org.quartz.scheduler.idleWaitTime"; 
PROP_SCHED_DB_FAILURE_RETRY_INTERVAL = "org.quartz.scheduler.dbFailureRetryInterval"; 
PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON = "org.quartz.scheduler.makeSchedulerThreadDaemon"; 
PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD = "org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer"; 
PROP_SCHED_CLASS_LOAD_HELPER_CLASS = "org.quartz.scheduler.classLoadHelper.class"; 
PROP_SCHED_JOB_FACTORY_CLASS = "org.quartz.scheduler.jobFactory.class"; 
PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN = "org.quartz.scheduler.interruptJobsOnShutdown"; 
PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT = "org.quartz.scheduler.interruptJobsOnShutdownWithWait"; 
PROP_THREAD_POOL_CLASS = "org.quartz.threadPool.class"; 
PROP_JOB_STORE_CLASS = "org.quartz.jobStore.class"; 
PROP_JOB_STORE_USE_PROP = "org.quartz.jobStore.useProperties"; 
PROP_CONNECTION_PROVIDER_CLASS = "connectionProvider.class";

Job 定時(shí)任務(wù)實(shí)例類

任務(wù)是一個(gè)實(shí)現(xiàn) org.quartz.Job 接口的類粘昨,任務(wù)類必須含有空構(gòu)造器垢啼,只有一個(gè)方法:

void execute(JobExecutionContext context) throws JobExecutionException;

當(dāng)關(guān)聯(lián)這個(gè)任務(wù)實(shí)例的觸發(fā)器表明的執(zhí)行時(shí)間到了的時(shí)候,調(diào)度程序 Scheduler 會(huì)調(diào)用這個(gè)方法來(lái)執(zhí)行任務(wù)张肾,任務(wù)內(nèi)容就可以在這個(gè)方法中執(zhí)行芭析。

public class HelloJob implements Job { 
    @Override 
    public void execute(JobExecutionContext context) throws JobExecutionException { 
        System.out.println(System.currentTimeMillis() + " - helloJob 任務(wù)執(zhí)行"); 
    } 
}

在該方法退出之前,會(huì)設(shè)置一個(gè)結(jié)果對(duì)象到 JobExecutionContext 中吞瞪。盡管這個(gè)結(jié)果對(duì) Quartz 來(lái)說沒什么意義馁启,但是 JobListeners 或者 TriggerListeners 可以監(jiān)聽查看 job 的執(zhí)行情況。芍秆。

JobDataMap 提供了一種“初始化成員屬性數(shù)據(jù)的機(jī)制”进统,在實(shí)現(xiàn)該 Job 接口的時(shí)候可能會(huì)用到。

Job 實(shí)例化的過程

首先創(chuàng)建一個(gè) Job 類浪听,在調(diào)度程序中可以創(chuàng)建很多個(gè) JobDetai,分別設(shè)置不同的 JobDataMap眉菱。

舉個(gè)例子說明迹栓,創(chuàng)建一個(gè)類 SalesReportJob 實(shí)現(xiàn) Job 接口,用做銷售報(bào)表使用俭缓】艘粒可以通過 JobDataMap 指定銷售員的名稱和銷售報(bào)表的依據(jù)等等。這就會(huì)創(chuàng)建多個(gè) JobDetails 了华坦,例如 SalesReportForJoe愿吹,SalesReportForMike 分別對(duì)應(yīng)在 JobDataMap 中指定的名字 joe 和 mike。

當(dāng)觸發(fā)器的執(zhí)行時(shí)間到了的時(shí)候惜姐,會(huì)加載與之關(guān)聯(lián)的 JobDetail犁跪,并在調(diào)度程序 Scheduler 中通過 JobFactory 的配置實(shí)例化它引用的 Job椿息。

JobFactory 調(diào)用 newInstance() 創(chuàng)建一個(gè)任務(wù)實(shí)例,然后調(diào)用 setter 方法設(shè)置在 JobDataMap 定義好的名字坷衍∏抻牛可以實(shí)現(xiàn) JobFactory,比如使用 IOC/DI 機(jī)制初始化的任務(wù)實(shí)例枫耳。

Job 的聲明和并發(fā)

以下對(duì)注解使用在 Job 實(shí)現(xiàn)類中乏矾,可以影響 Quartz 的行為:

@DisallowConcurrentExecution

告訴 Quartz 不要執(zhí)行多個(gè)任務(wù)實(shí)例。

在上面的 SalesReportJob 類添加該注解迁杨,將會(huì)只有一個(gè) SalesReportForJoe 實(shí)例在給定的時(shí)間執(zhí)行钻心,但是 SalesReportForMike 是可以執(zhí)行的。這個(gè)約束是基于 JobDetail 的铅协,而不是基于任務(wù)類的捷沸。

@PersistJobDataAfterExecution

告訴 Quartz 在任務(wù)執(zhí)行成功完畢之后(沒有拋出異常),修改 JobDetail 的 JobDataMap 備份警医,以供下一個(gè)任務(wù)使用亿胸。

如果使用了 @PersistJobDataAfterExecution 注解,強(qiáng)烈建議同時(shí)使用 @DisallowConcurrentExecution 注解预皇,以避免當(dāng)兩個(gè)同樣的 Job 并發(fā)執(zhí)行的時(shí)候產(chǎn)生的存儲(chǔ)數(shù)據(jù)混亂侈玄。

Job 的其他屬性

  • 持久化,如果一個(gè)任務(wù)不是持久化的吟温,則當(dāng)沒有觸發(fā)器關(guān)聯(lián)它的時(shí)候序仙,Quartz 會(huì)從 scheduler 中刪除它。
  • 請(qǐng)求恢復(fù)鲁豪,如果一個(gè)任務(wù)請(qǐng)求恢復(fù)潘悼,一般是該任務(wù)執(zhí)行期間發(fā)生了系統(tǒng)崩潰或者其他關(guān)閉進(jìn)程的操作,當(dāng)服務(wù)再次啟動(dòng)的時(shí)候爬橡,會(huì)再次執(zhí)行該任務(wù)治唤。這種情況下,JobExecutionContext.isRecovering() 會(huì)返回 true糙申。

JobDetail 定義任務(wù)實(shí)例的一些屬性特征

org.quartz.JobDetail 接口負(fù)責(zé)傳輸給定的任務(wù)實(shí)例的屬性到 Scheduler宾添。JobDetail 是通過 JobBuilder 創(chuàng)建的。
Quartz 不會(huì)存儲(chǔ)一個(gè)真實(shí)的 Job 類實(shí)例柜裸,但是允許通過 JobDetail 定義一個(gè)任務(wù)實(shí)例缕陕。
任務(wù)有一個(gè)名稱 name 和 group 來(lái)關(guān)聯(lián),在一個(gè) Scheduler 中這二者的組合必須是唯一的疙挺。
多個(gè)觸發(fā)器可以指向同一個(gè) Job扛邑,但一個(gè)觸發(fā)器只能指向一個(gè) Job。

JobDataMap 任務(wù)數(shù)據(jù)映射

JobDataMap 用來(lái)保存任務(wù)實(shí)例的狀態(tài)信息铐然。當(dāng)一個(gè) Job 被添加到 scheduler 的時(shí)候蔬崩,JobDataMap 實(shí)例就會(huì)存儲(chǔ)一次關(guān)于該任務(wù)的狀態(tài)信息數(shù)據(jù)恶座。也可以使用 @PersistJobDataAfterExecution 注解標(biāo)明在一個(gè)任務(wù)執(zhí)行完畢之后就存儲(chǔ)一次。

JobDataMap 實(shí)例也可以存儲(chǔ)一個(gè)觸發(fā)器舱殿。這是非常有用的奥裸,特別是當(dāng)任務(wù)被多個(gè)觸發(fā)器引用的時(shí)候,根據(jù)不同的觸發(fā)時(shí)機(jī)沪袭,你可以提供不同的輸入條件湾宙。

JobExecutionContext 也可以再執(zhí)行時(shí)包含一個(gè) JobDataMap ,合并了觸發(fā)器的 JobDataMap (如果有的話)和 Job 的 JobDataMap (如果有的話)冈绊。

在定義 JobDetail 的時(shí)候侠鳄,將一些數(shù)據(jù)放入JobDataMap 中:

JobDetail job = newJob(HelloJob.class) 
    .withIdentity("job1", "group1") 
    .usingJobData("jobSays", "Hello World!") 
    .usingJobData("myFloatValue", 3.141f) 
    .build();

在任務(wù)執(zhí)行的時(shí)候,可以獲取 JobDataMap 中的數(shù)據(jù):

@Override 
public void execute(JobExecutionContext context) throws JobExecutionException { 
    JobKey key = context.getJobDetail().getKey(); 
    JobDataMap dataMap = context.getJobDetail().getJobDataMap(); 
    String jobSays = dataMap.getString("jobSays"); 
    float myFloatValue = dataMap.getFloat("myFloatValue"); 
    // ... 
}

在觸發(fā)器也添加數(shù)據(jù):

Trigger trigger = newTrigger() 
    .withIdentity("trigger1", "group1") 
    .usingJobData("trigger_key", "每2秒執(zhí)行一次") 
    .startNow() 
    .withSchedule(simpleSchedule() 
        .withIntervalInSeconds(2) 
        .repeatForever()) 
    .build();

任務(wù)執(zhí)行方法可以獲取到合并后的數(shù)據(jù)

@Override 
public void execute(JobExecutionContext context) throws JobExecutionException { 
    JobKey key = context.getJobDetail().getKey(); 
    
    // 使用歸并的 JobDataMap 
    JobDataMap dataMap = context.getMergedJobDataMap(); 
    String jobSays = dataMap.getString("jobSays"); 
    float myFloatValue = dataMap.getFloat("myFloatValue"); 
    String triggerSays = dataMap.getString("trigger_key"); 
    // ... 
}

Trigger 觸發(fā)器

觸發(fā)器使用 TriggerBuilder 來(lái)實(shí)例化死宣,有一個(gè) TriggerKey 關(guān)聯(lián)伟恶,在一個(gè) Scheduler 中必須是唯一的。
多個(gè)觸發(fā)器可以指向同一個(gè)工作毅该,但一個(gè)觸發(fā)器只能指向一個(gè)工作博秫。
觸發(fā)器可以傳送數(shù)據(jù)給 job,通過將數(shù)據(jù)放進(jìn)觸發(fā)器的 JobDataMap眶掌。

觸發(fā)器常用屬性

觸發(fā)器也有很多屬性挡育,這些屬性都是在使用 TriggerBuilder 定義觸發(fā)器時(shí)設(shè)置的。

  • TriggerKey朴爬,唯一標(biāo)識(shí)即寒,在一個(gè) Scheduler 中必須是唯一的
  • startTime,開始時(shí)間召噩,通常使用 startAt(java.util.Date)
  • endTime母赵,結(jié)束時(shí)間,設(shè)置了結(jié)束時(shí)間則在這之后具滴,不再觸發(fā)

觸發(fā)器的優(yōu)先級(jí)

有時(shí)候凹嘲,會(huì)安排很多任務(wù),但是 Quartz 并沒有更多的資源去處理它构韵。這種情況下施绎,必須需要很好地控制哪個(gè)任務(wù)先執(zhí)行。這時(shí)候可以設(shè)置 priority 屬性(使用方法 withPriority(int))來(lái)控制觸發(fā)器的優(yōu)先級(jí)贞绳。
優(yōu)先級(jí)只有觸發(fā)器出發(fā)時(shí)間一樣的時(shí)候才有意義。
當(dāng)一個(gè)任務(wù)請(qǐng)求恢復(fù)執(zhí)行時(shí)致稀,它的優(yōu)先級(jí)和原始優(yōu)先級(jí)是一樣的冈闭。

JobBuilder 用于創(chuàng)建 JobDetail,TriggerBuilder 用于創(chuàng)建觸發(fā)器 Trigger

JobBuilder 用于創(chuàng)建 JobDetail抖单,如果沒有調(diào)用 withIdentity(..) 指定 job 的名字萎攒,會(huì)自動(dòng)生成一個(gè)遇八。
TriggerBuilder 用于創(chuàng)建 Trigger,如果沒有調(diào)用 withSchedule(..) 方法耍休,會(huì)使用默認(rèn)的 schedule 刃永。如果沒有使用 withIdentity(..) 會(huì)自動(dòng)生成一個(gè)觸發(fā)器名稱。

Quartz 通過一種領(lǐng)域特定語(yǔ)言(DSL)提供了一種自己的 builder 的風(fēng)格API來(lái)創(chuàng)建任務(wù)調(diào)度相關(guān)的實(shí)體羊精。DSL 可以通過對(duì)類的靜態(tài)方法的使用來(lái)調(diào)用:TriggerBuilder斯够、JobBuilder、DateBuilder喧锦、JobKey读规、TriggerKey 以及其它的關(guān)于 Schedule 創(chuàng)建的實(shí)現(xiàn)。

// import static org.quartz.JobBuilder.newJob; 
// import static org.quartz.SimpleScheduleBuilder.simpleSchedule; 
// import static org.quartz.TriggerBuilder.newTrigger; 

JobDetail job = newJob(MyJob.class) 
    .withIdentity("myJob") 
    .build(); 

Trigger trigger = newTrigger() 
    .withIdentity(triggerKey("myTrigger", "myTriggerGroup")) 
    .withSchedule(simpleSchedule() 
        .withIntervalInHours(1) 
        .repeatForever()) 
    .startAt(futureDate(10, MINUTES)) 
    .build(); 

scheduler.scheduleJob(job, trigger);

Cron 表達(dá)式的使用

Quartz 提供了多種觸發(fā)器:

Trigger

最常用的兩種觸發(fā)器:簡(jiǎn)單觸發(fā)器 SimpleTrigger燃少、基于 Cron 表達(dá)式的觸發(fā)器 CronTrigger

簡(jiǎn)單觸發(fā)器 SimpleTrigger

SimpleTrigger 是接口 Trigger 的一個(gè)具體實(shí)現(xiàn)束亏,可以觸發(fā)一個(gè)已經(jīng)安排進(jìn)調(diào)度程序的任務(wù),并可以指定時(shí)間間隔重復(fù)執(zhí)行該任務(wù)阵具。

SimpleTrigger 包含幾個(gè)特點(diǎn):開始時(shí)間碍遍、結(jié)束時(shí)間、重復(fù)次數(shù)以及重復(fù)執(zhí)行的時(shí)間間隔阳液。

重復(fù)的次數(shù)可以是零怕敬,一個(gè)正整數(shù),或常量 SimpleTrigger.REPEAT_INDEFINITELY趁舀。

重復(fù)執(zhí)行的時(shí)間間隔可以是零赖捌,或者 long 類型的數(shù)值表示毫秒。值得注意的是矮烹,零重復(fù)間隔會(huì)造成觸發(fā)器同時(shí)發(fā)生(或接近同時(shí))越庇。

結(jié)束時(shí)間的會(huì)重寫重復(fù)的次數(shù),這可能是有用的奉狈,如果你想創(chuàng)建一個(gè)觸發(fā)器卤唉,如每10秒觸發(fā)一次,直到一個(gè)給定的時(shí)刻仁期,而不是要計(jì)算的次數(shù)桑驱,它會(huì)在開始時(shí)間和結(jié)束時(shí)間重復(fù)執(zhí)行。結(jié)束時(shí)間一到跛蛋,就算你指定了重復(fù)次數(shù)很多次(比如執(zhí)行10W次)熬的,但是時(shí)間一到它將不再執(zhí)行。

SimpleTrigger 實(shí)例創(chuàng)建依賴于 TriggerBuilder 和 SimpleScheduleBuilder 赊级,使用 Quartz 提供的DSL風(fēng)格創(chuàng)建觸發(fā)器實(shí)例押框,

import static org.quartz.TriggerBuilder.*; 
import static org.quartz.SimpleScheduleBuilder.*; 
import static org.quartz.DateBuilder.*:

可以創(chuàng)建很多不同形式的觸發(fā)器:

創(chuàng)建一個(gè)指定時(shí)間開始執(zhí)行,但是不重復(fù)的觸發(fā)器理逊,使用 startAt(java.util.Date) 設(shè)置觸發(fā)器的第一次執(zhí)行時(shí)間:

SimpleTrigger trigger = (SimpleTrigger) newTrigger() 
    .withIdentity("trigger1", "group1") 
    .startAt(myStartTime) 
    .forJob("job1", "group1") 
    .build();

創(chuàng)建一個(gè)指定時(shí)間開始執(zhí)行橡伞,每10s執(zhí)行一次盒揉,共執(zhí)行10次的觸發(fā)器
使用 SimpleScheduleBuilder.withIntervalInSeconds(N) 方法可以指定間隔N秒就執(zhí)行一次;withRepeatCount(M) 可以指定執(zhí)行次數(shù)M兑徘。

SimpleScheduleBuilder 有很多類似的方法刚盈。

Trigger trigger = newTrigger() 
    .withIdentity("trigger3", "group1") 
    .startAt(myTimeToStartFiring) 
    .withSchedule( 
        simpleSchedule() 
            .withIntervalInSeconds(10) 
            .withRepeatCount(10) 
    ) 
    .forJob(myJob) 
    .build();

創(chuàng)建一個(gè)在未來(lái)第五分鐘的時(shí)候執(zhí)行一次的觸發(fā)器

使用 DateBuilder 的 futureDate 方法可以指定在未來(lái)時(shí)間執(zhí)行。

Trigger trigger = (SimpleTrigger) newTrigger() 
    .withIdentity("trigger5", "group1") 
    .startAt(futureDate(5, IntervalUnit.MINUTE)) 
    .forJob(myJobKey) 
    .build();

創(chuàng)建一個(gè)馬上執(zhí)行挂脑,每隔5分鐘執(zhí)行藕漱、直到22:00結(jié)束執(zhí)行的觸發(fā)器
使用 TriggerBuilder 的 startNow() 方法立即觸發(fā)(scheduler 調(diào)用 start 時(shí)算起,視優(yōu)先級(jí)而定)最域;
withIntervalInMinutes(5) 每5分鐘執(zhí)行一次谴分;
repeatForever() 一直重復(fù);
endAt(dateOf(22, 0, 0)) 直到22:00終結(jié)觸發(fā)器:

Trigger trigger = newTrigger() 
    .withIdentity("trigger7", "group1") 
    .startNow() 
    .withSchedule( 
        simpleSchedule() 
            .withIntervalInMinutes(5) 
            .repeatForever() 
    ) 
    .endAt(dateOf(22, 0, 0)) 
    .build();

創(chuàng)建一個(gè)在偶數(shù)小時(shí)執(zhí)行镀脂、每?jī)蓚€(gè)小時(shí)執(zhí)行一次的觸發(fā)器

Trigger trigger = newTrigger() 
    .withIdentity("trigger8") 
    .startAt(evenHourDate(null)) 
    .withSchedule(simpleSchedule().withIntervalInHours(2).repeatForever())     
    .build();

值得注意的是牺蹄,如果沒有調(diào)用 startAt(..) 方法,默認(rèn)使用 startNow()薄翅。

關(guān)于簡(jiǎn)單觸發(fā)器“熄火(misfire)”的指令

SimpleTrigger 包含一些指令在“熄火”時(shí)可以告知 Quartz 怎么去處理沙兰。這些指令包含在 SimpleTrigger 的常量中。

  • REPEAT_INDEFINITELY - 用于表示觸發(fā)器的“重復(fù)計(jì)數(shù)”是不確定的翘魄《μ欤或者換句話說,觸發(fā)應(yīng)該不斷重復(fù)直到觸發(fā)的結(jié)尾時(shí)間戳

  • MISFIRE_INSTRUCTION_FIRE_NOW - 如果熄火暑竟,該指令會(huì)告訴 Quartz 應(yīng)該馬上再次觸發(fā)

  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT - 如果熄火斋射,該指令會(huì)告訴 Quartz 馬上執(zhí)行并計(jì)數(shù)累計(jì)到已經(jīng)執(zhí)行的次數(shù)當(dāng)中去,如果結(jié)束時(shí)間已經(jīng)過了但荤,則不會(huì)再執(zhí)行罗岖。

  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT - 如果熄火,會(huì)告訴 Quartz 想要現(xiàn)在就執(zhí)行一次(即使現(xiàn)在不是它原本計(jì)劃的觸發(fā)時(shí)間)

  • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT - 如果熄火腹躁,會(huì)告訴 Quartz 在下一次執(zhí)行時(shí)間再次開始執(zhí)行

一個(gè)使用“熄火”策略的觸發(fā)器示例:

Trigger trigger = newTrigger() 
    .withIdentity("trigger7", "group1") 
    .withSchedule(
        simpleSchedule()
            .withIntervalInMinutes(5)
            .repeatForever() 
            .withMisfireHandlingInstructionNextWithExistingCount() 
    ) 
    .build();

基于 Cron 表達(dá)式的觸發(fā)器 CronTrigger

CronTrigger 通常使用得比 SimpleTrigger 多一些桑包。特別是基于日歷的概念,而不是對(duì)具體間隔的行為纺非。

通過 CronTrigger哑了,可以指定“每個(gè)星期五的中午”、“每個(gè)工作日上午9:30”烧颖,“一月的每星期一的上午9點(diǎn)至10點(diǎn)之間的每5分鐘弱左,星期三和星期五” 執(zhí)行。

Cron 表達(dá)式

首先了解 Cron 表達(dá)式炕淮,是用于配制 CronTrigger 實(shí)例的科贬。Cron 表達(dá)式,實(shí)際上是由七個(gè)子表達(dá)式組成的字符串,它描述了不同的調(diào)度細(xì)節(jié)榜掌。這些子表達(dá)式是用空格分隔的,并表示:

秒 
分 
時(shí) 
月中的天 
月 
周中的天 
年(可選項(xiàng))

例如: “0 0 12 ? * WED” 表示 “每個(gè)星期三的12點(diǎn)”乘综,單個(gè)子表達(dá)式可以包含范圍和/或列表憎账,例如:

"0 0 7 ? * MON-FRI" 表示 "每個(gè)工作日的7點(diǎn)" 
"0 0 19 ? * MON,WED,FRI" 表示 "周一、周三和周五的19點(diǎn)" 
"0 0 14 ? * MON-WED,SAT" 表示 "周一到周三以及周六的14點(diǎn)"

Cron 表達(dá)式的規(guī)則說明

所有字段都有一組可以指定的有效值卡辰。

  • 數(shù)字 0 到 59 可以表示秒和分
  • 0到23可以表示小時(shí)
  • 月中的天可以使用1到31的數(shù)值, 但是你要注意該月的天數(shù)!
  • 月用0 到 11之間的數(shù)值表示, 或者使用JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 和 DEC來(lái)表示1-12月
  • 一周中的天試用1到7表示 (1 表示 周日) 或者使用 SUN, MON, TUE, WED, THU, FRI 和 SAT

創(chuàng)建 CronTrigger

CronTrigger 實(shí)例使用 TriggerBuilder 和 CronScheduleBuilder 創(chuàng)建

創(chuàng)建一個(gè)8到17點(diǎn)間每?jī)煞昼妶?zhí)行一次的 Cron 觸發(fā)器:

Trigger cronTrigger1 = newTrigger() 
    .withIdentity("trigger3", "group1") 
    .withSchedule(cronSchedule("0 0/2 8-17 * * ?")) 
    .forJob("myJob", "group1") 
    .build();

創(chuàng)建一個(gè)每天10:42執(zhí)行的Cron觸發(fā)器:

Trigger cronTrigger2 = newTrigger() 
    .withIdentity("trigger3", "group1") 
    .withSchedule(dailyAtHourAndMinute(10, 42)) 
    .forJob(job.getKey()) 
    .build(); 

Trigger cronTrigger3 = newTrigger() 
    .withIdentity("trigger3", "group1") 
    .withSchedule(cronSchedule("0 42 10 * * ?")) 
    .forJob(job.getKey()) 
    .build();

關(guān)于 Cron 觸發(fā)器“熄火”的指令

CronTrigger 同樣包含一些指令在它“熄火”時(shí)可以告知 Quartz 怎么去處理胞皱。

  • MISFIRE_INSTRUCTION_FIRE_ONCE_NOW - 如果熄火,該指令會(huì)告訴 Quartz 希望馬上再次觸發(fā)

  • MISFIRE_INSTRUCTION_DO_NOTHING - 如果熄火九妈,該指令會(huì)告訴 Quartz 下一次執(zhí)行時(shí)間到來(lái)時(shí)再執(zhí)行反砌,并不想馬上執(zhí)行

Trigger cronTrigger4MisfireInstruction = newTrigger() 
    .withIdentity("trigger3", "group1") 
    .withSchedule(cronSchedule("0 0/2 8-17 * * ?")       
        .withMisfireHandlingInstructionFireAndProceed()) 
    .forJob("myJob", "group1") 
    .build();

常用的 Cron 表達(dá)式例子:

0 0 12 * * ? 每天12點(diǎn)執(zhí)行 
0 15 10 ? * * 每天的10:15執(zhí)行 
0 15 10 * * ? 每天的10:15執(zhí)行 
0 15 10 * * ? * 每天的10:15執(zhí)行 
0 15 10 * * ? 2005 2005年每天的10:15執(zhí)行 
0 * 14 * * ? 每天的14:00到14:59期間每分鐘執(zhí)行 
0 0/5 14 * * ? 每天的14:00到14:55每隔5分鐘執(zhí)行 
0 0/5 14,18 * * ? 每天的14:00到14:55每隔5分鐘執(zhí)行和18:00到18:55每隔5分鐘執(zhí)行 
0 0-5 14 * * ? 每天的14:00到14:05執(zhí)行 
0 10,44 14 ? 3 WED 三月的每一個(gè)周三的14:10和14:44執(zhí)行 
0 15 10 ? * MON-FRI 工作日每天的10:15:00執(zhí)行 
0 15 10 15 * ? 每個(gè)月的第15天的10:15:00執(zhí)行 
0 15 10 L * ? 每個(gè)月最后一天的10:15:00執(zhí)行 
0 15 10 ? * 6L 每個(gè)月最后一個(gè)周五的10:15:00執(zhí)行 
0 15 10 ? * 6L 2002-2005 2002, 2003, 2004, 和2005年每個(gè)月最后一個(gè)周五的10:15:00執(zhí)行 
0 15 10 ? * 6#3 每個(gè)月的第三個(gè)周五的10:15:00執(zhí)行 
0 0 12 1/5 * ? 每個(gè)月的第一天的12:00:00開始執(zhí)行,每隔5天間隔執(zhí)行 
0 11 11 11 11 ? 每年的11月11日11:11:00執(zhí)行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萌朱,一起剝皮案震驚了整個(gè)濱河市宴树,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晶疼,老刑警劉巖酒贬,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翠霍,居然都是意外死亡锭吨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門寒匙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)零如,“玉大人,你說我怎么就攤上這事锄弱】祭伲” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵棵癣,是天一觀的道長(zhǎng)辕翰。 經(jīng)常有香客問我,道長(zhǎng)狈谊,這世上最難降的妖魔是什么喜命? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮河劝,結(jié)果婚禮上壁榕,老公的妹妹穿的比我還像新娘。我一直安慰自己赎瞎,他們只是感情好牌里,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般牡辽。 火紅的嫁衣襯著肌膚如雪喳篇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天态辛,我揣著相機(jī)與錄音麸澜,去河邊找鬼。 笑死奏黑,一個(gè)胖子當(dāng)著我的面吹牛炊邦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播熟史,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼馁害,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蹂匹?” 一聲冷哼從身側(cè)響起碘菜,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤牵祟,失蹤者是張志新(化名)和其女友劉穎郑象,沒想到半個(gè)月后魂贬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驾茴,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡落萎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年侥加,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缓淹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亏较。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡静尼,死狀恐怖白粉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鼠渺,我是刑警寧澤鸭巴,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站拦盹,受9級(jí)特大地震影響鹃祖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜普舆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一恬口、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沼侣,春花似錦祖能、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春钞螟,著一層夾襖步出監(jiān)牢的瞬間兔甘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工鳞滨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裂明,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓太援,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扳碍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子提岔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • scheduler定時(shí)調(diào)度系統(tǒng)是大多行業(yè)項(xiàng)目都需要的,傳統(tǒng)的spring-job模式笋敞,個(gè)人感覺已經(jīng)out了碱蒙,因?yàn)榇?..
    安琪拉_4b7e閱讀 2,835評(píng)論 4 6
  • Quartz是什么 Quartz是一個(gè)開源的作業(yè)調(diào)度包,能夠運(yùn)行在幾乎任何java項(xiàng)目中夯巷,小到單機(jī)應(yīng)用赛惩,大到電商系...
    零度沸騰_yjz閱讀 3,128評(píng)論 0 9
  • 在半個(gè)月之前,有幸看了xxl-job源碼趁餐,原本打算寫一篇源碼分析文章喷兼。結(jié)果由于瑣碎的事情干擾了,擱淺了后雷。本篇文章先...
    cmazxiaoma閱讀 15,796評(píng)論 3 95
  • Quartz 主要API Scheduler 任務(wù)調(diào)度器季惯,按照特定的觸發(fā)規(guī)則,自動(dòng)執(zhí)行任務(wù) Job 接口臀突,定義需要...
    Impler閱讀 1,162評(píng)論 0 0
  • 什么是定時(shí)任務(wù)調(diào)度 基于給定的時(shí)間點(diǎn)勉抓,給定的時(shí)間間隔或者給定的執(zhí)行次數(shù)自動(dòng)完成執(zhí)行任務(wù) 在Java中的定時(shí)調(diào)度工具...
    Hey_Shaw閱讀 2,508評(píng)論 2 1