注:本文來源於:是Guava不是瓜娃 《定時任務框架Quartz-(一)Quartz入門與Demo搭建》java
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,徹底由Java開發,能夠用來執行定時任務,相似於java.util.Timer。可是相較於Timer, Quartz增長了不少功能: 持久性做業 - 就是保持調度定時的狀態; 做業管理 - 對調度做業進行有效的管理;
大部分公司都會用到定時任務這個功能。
拿火車票購票來講,當你下單後,後臺就會插入一條待支付的task(job),通常是30分鐘,超過30min後就會執行這個job,去判斷你是否支付,未支付就會取消這次訂單;當你支付完成以後,後臺拿到支付回調後就會再插入一條待消費的task(job),Job觸發日期爲火車票上的出發日期,超過這個時間就會執行這個job,判斷是否使用等。併發
在咱們實際的項目中,當Job過多的時候,確定不能人工去操做,這時候就須要一個任務調度框架,幫咱們自動去執行這些程序。那麼該如何實現這個功能呢?框架
(1)首先咱們須要定義實現一個定時功能的接口,咱們能夠稱之爲Task(或Job),如定時發送郵件的task(Job),重啓機器的task(Job),優惠券到期發送短信提醒的task(Job),實現接口以下:
dom
(2)有了任務以後,還須要一個可以實現觸發任務去執行的觸發器,觸發器Trigger最基本的功能是指定Job的執行時間,執行間隔,運行次數等。ide
(3)有了Job和Trigger後,怎麼樣將二者結合起來呢?即怎樣指定Trigger去執行指定的Job呢?這時須要一個Schedule,來負責這個功能的實現。工具
上面三個部分就是Quartz的基本組成部分:測試
下面來利用Quartz搭建一個最基本的Demo。
ui
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; /** * 新建一個可以打印任意內容的Job: * @author admin * */ 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)); } }
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.util.concurrent.TimeUnit; 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; /** * 建立Schedule,執行任務: * @author admin * */ public class MyScheduler { public static void main(String[] args) throws SchedulerException, InterruptedException { // 一、建立調度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 二、建立JobDetail實例,並與PrintWordsJob類綁定(Job執行內容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class).withIdentity("job1", "group1").build(); // 三、構建Trigger實例,每隔1s執行一次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .startNow()//當即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s執行一次 .repeatForever()).build();//一直執行 //四、執行 scheduler.scheduleJob(jobDetail, trigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); //睡眠 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println("--------scheduler shutdown ! ------------"); } }
運行程序,能夠看到程序每隔1s會打印出內容,且在一分鐘後結束:spa
下面就程序中出現的幾個參數,看一下Quartz框架中的幾個重要參數:.net
Job和JobDetail
JobExecutionContext
JobDataMap
Trigger、SimpleTrigger、CronTrigger
Job是Quartz中的一個接口,接口下只有execute方法,在這個方法中編寫業務邏輯。
接口中的源碼:
JobDetail用來綁定Job,爲Job實例提供許多屬性:
name
group
jobClass
jobDataMap
JobDetail綁定指定的Job,每次Scheduler調度執行一個Job的時候,首先會拿到對應的Job,而後建立該Job實例,再去執行Job中的execute()的內容,任務執行結束後,關聯的Job對象實例會被釋放,且會被JVM GC清除。
爲何設計成JobDetail + Job,不直接使用Job
JobDetail定義的是任務數據,而真正的執行邏輯是在Job中。 這是由於任務是有可能併發執行,若是Scheduler直接使用Job,就會存在對同一個Job實例併發訪問的問題。而JobDetail & Job 方式,Sheduler每次執行,都會根據JobDetail建立一個新的Job實例,這樣就能夠規避併發訪問的問題。
JobExecutionContext中包含了Quartz運行時的環境以及Job自己的詳細數據信息。
當Schedule調度執行一個Job的時候,就會將JobExecutionContext傳遞給該Job的execute()中,Job就能夠經過JobExecutionContext對象獲取信息。
主要信息有:
JobDataMap實現了JDK的Map接口,能夠以Key-Value的形式存儲數據。
JobDetail、Trigger均可以使用JobDataMap來設置一些參數或信息,
Job執行execute()方法的時候,JobExecutionContext能夠獲取到JobExecutionContext中的信息:
如:
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class).usingJobData("jobDetail1", "這個Job用來測試的") .withIdentity("job1", "group1").build(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "這是jobDetail1的trigger") .startNow()//當即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s執行一次 .repeatForever()).build();//一直執行
Job執行的時候,能夠獲取到這些參數信息:
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetail1")); System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("trigger1")); 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)); }
Trigger是Quartz的觸發器,會去通知Scheduler什麼時候去執行對應Job。
new Trigger().startAt():表示觸發器首次被觸發的時間; new Trigger().endAt():表示觸發器結束觸發的時間;
Date startDate = new Date(); startDate.setTime(startDate.getTime() + 5000); Date endDate = new Date(); endDate.setTime(startDate.getTime() + 5000); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "這是jobDetail1的trigger") .startNow()//當即生效 .startAt(startDate) .endAt(endDate) .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s執行一次 .repeatForever()).build();//一直執行
CronTrigger
CronTrigger功能很是強大,是基於日曆的做業調度,而SimpleTrigger是精準指定間隔,因此相比SimpleTrigger,CroTrigger更加經常使用。CroTrigger是基於Cron表達式的,先了解下Cron表達式:
由7個子表達式組成字符串的,格式以下:
[秒] [分] [小時] [日] [月] [周] [年]
Cron表達式的語法比較複雜,
如:* 30 10 ? * 1/5 *
表示(從後往前看)
[指定年份] 的[ 週一到週五][指定月][不指定日][上午10時][30分][指定秒]
又如:00 00 00 ? * 10,11,12 1#5 2018
表示2018年十、十一、12月的第一週的星期五這一天的0時0分0秒去執行任務。
下面是給的一個例子:
可經過在線生成Cron表達式的工具:http://cron.qqe2.com/ 來生成本身想要的表達式。
下面的代碼就實現了每週一到週五上午10:30執行定時任務
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.util.Date; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; /** * 下面的代碼就實現了每週一到週五上午10:30執行定時任務 * @author admin * */ public class MyScheduler2 { public static void main(String[] args) throws SchedulerException, InterruptedException { // 一、建立調度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 二、建立JobDetail實例,並與PrintWordsJob類綁定(Job執行內容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class) .usingJobData("jobDetail1", "這個Job用來測試的") .withIdentity("job1", "group1").build(); // 三、構建Trigger實例,每隔1s執行一次 Date startDate = new Date(); startDate.setTime(startDate.getTime() + 5000); Date endDate = new Date(); endDate.setTime(startDate.getTime() + 5000); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "這是jobDetail1的trigger") .startNow()//當即生效 .startAt(startDate) .endAt(endDate) .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018")) .build(); //四、執行 scheduler.scheduleJob(jobDetail, cronTrigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); System.out.println("--------scheduler shutdown ! ------------"); } }