詳解定時(shí)任務(wù)框架Quartz的使用
一、什么是Quartz
什么是Quartz?
Quartz是OpenSymphony開源組織在Job scheduling領(lǐng)域又一個(gè)開源項(xiàng)目,完全由Java開發(fā),可以用來執(zhí)行定時(shí)任務(wù),類似于java.util.Timer。但是相較于Timer, Quartz增加了很多功能:
- 持久性作業(yè) – 就是保持調(diào)度定時(shí)的狀態(tài);
- 作業(yè)管理 – 對調(diào)度作業(yè)進(jìn)行有效的管理;
大部分公司都會用到定時(shí)任務(wù)這個(gè)功能。 拿火車票購票來說,當(dāng)你下單后,后臺就會插入一條待支付的task(job),一般是30分鐘,超過30min后就會執(zhí)行這個(gè)job,去判斷你是否支付,未支付就會取消此次訂單;當(dāng)你支付完成之后,后臺拿到支付回調(diào)后就會再插入一條待消費(fèi)的task(job),Job觸發(fā)日期為火車票上的出發(fā)日期,超過這個(gè)時(shí)間就會執(zhí)行這個(gè)job,判斷是否使用等。
在我們實(shí)際的項(xiàng)目中,當(dāng)Job過多的時(shí)候,肯定不能人工去操作,這時(shí)候就需要一個(gè)任務(wù)調(diào)度框架,幫我們自動去執(zhí)行這些程序。那么該如何實(shí)現(xiàn)這個(gè)功能呢?
(1)首先我們需要定義實(shí)現(xiàn)一個(gè)定時(shí)功能的接口,我們可以稱之為Task(或Job),如定時(shí)發(fā)送郵件的task(Job),重啟機(jī)器的task(Job),優(yōu)惠券到期發(fā)送短信提醒的task(Job),實(shí)現(xiàn)接口如下:
(2)有了任務(wù)之后,還需要一個(gè)能夠?qū)崿F(xiàn)觸發(fā)任務(wù)去執(zhí)行的觸發(fā)器,觸發(fā)器Trigger最基本的功能是指定Job的執(zhí)行時(shí)間,執(zhí)行間隔,運(yùn)行次數(shù)等。
(3)有了Job和Trigger后,怎么樣將兩者結(jié)合起來呢?即怎樣指定Trigger去執(zhí)行指定的Job呢?這時(shí)需要一個(gè)Schedule,來負(fù)責(zé)這個(gè)功能的實(shí)現(xiàn)。
上面三個(gè)部分就是Quartz的基本組成部分:
- 調(diào)度器:Scheduler
- 任務(wù):JobDetail
- 觸發(fā)器:Trigger,包括SimpleTrigger和CronTrigger
二、Quartz Demo搭建
下面來利用Quartz搭建一個(gè)最基本的Demo。
1、導(dǎo)入依賴的jar包:
org.quartz-scheduler quartz 2.3.0
2、新建一個(gè)能夠打印任意內(nèi)容的Job:
/** * Created by wanggenshen * Date: on 2018/7/7 16:28. * Description: 打印任意內(nèi)容 */ 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)); } }
3、創(chuàng)建Schedule,執(zhí)行任務(wù):
/** * Created by wanggenshen * Date: on 2018/7/7 16:31. * Description: XXX */ public class MyScheduler { public static void main(String[] args) throws SchedulerException, InterruptedException { // 1、創(chuàng)建調(diào)度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 2、創(chuàng)建JobDetail實(shí)例,并與PrintWordsJob類綁定(Job執(zhí)行內(nèi)容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class) .withIdentity("job1", "group1").build(); // 3、構(gòu)建Trigger實(shí)例,每隔1s執(zhí)行一次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .startNow()//立即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s執(zhí)行一次 .repeatForever()).build();//一直執(zhí)行 //4、執(zhí)行 scheduler.scheduleJob(jobDetail, trigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); //睡眠 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println("--------scheduler shutdown ! ------------"); } }
運(yùn)行程序,可以看到程序每隔1s會打印出內(nèi)容,且在一分鐘后結(jié)束:
三、Quartz核心詳解
下面就程序中出現(xiàn)的幾個(gè)參數(shù),看一下Quartz框架中的幾個(gè)重要參數(shù):
- Job和JobDetail
- JobExecutionContext
- JobDataMap
- Trigger、SimpleTrigger、CronTrigger
(1)Job和JobDetail Job是Quartz中的一個(gè)接口,接口下只有execute方法,在這個(gè)方法中編寫業(yè)務(wù)邏輯。 接口中的源碼:
JobDetail用來綁定Job,為Job實(shí)例提供許多屬性:
- name
- group
- jobClass
- jobDataMap
JobDetail綁定指定的Job,每次Scheduler調(diào)度執(zhí)行一個(gè)Job的時(shí)候,首先會拿到對應(yīng)的Job,然后創(chuàng)建該Job實(shí)例,再去執(zhí)行Job中的execute()的內(nèi)容,任務(wù)執(zhí)行結(jié)束后,關(guān)聯(lián)的Job對象實(shí)例會被釋放,且會被JVM GC清除。
為什么設(shè)計(jì)成JobDetail + Job,不直接使用Job
JobDetail定義的是任務(wù)數(shù)據(jù),而真正的執(zhí)行邏輯是在Job中。 這是因?yàn)槿蝿?wù)是有可能并發(fā)執(zhí)行,如果Scheduler直接使用Job,就會存在對同一個(gè)Job實(shí)例并發(fā)訪問的問題。而JobDetail & Job 方式,Sheduler每次執(zhí)行,都會根據(jù)JobDetail創(chuàng)建一個(gè)新的Job實(shí)例,這樣就可以規(guī)避并發(fā)訪問的問題。
(2)JobExecutionContext
JobExecutionContext中包含了Quartz運(yùn)行時(shí)的環(huán)境以及Job本身的詳細(xì)數(shù)據(jù)信息。 當(dāng)Schedule調(diào)度執(zhí)行一個(gè)Job的時(shí)候,就會將JobExecutionContext傳遞給該Job的execute()中,Job就可以通過JobExecutionContext對象獲取信息。 主要信息有:
(3)JobExecutionContext JobDataMap實(shí)現(xiàn)了JDK的Map接口,可以以Key-Value的形式存儲數(shù)據(jù)。 JobDetail、Trigger都可以使用JobDataMap來設(shè)置一些參數(shù)或信息, Job執(zhí)行execute()方法的時(shí)候,JobExecutionContext可以獲取到JobExecutionContext中的信息: 如:
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class) .usingJobData("jobDetail1", "這個(gè)Job用來測試的") .withIdentity("job1", "group1").build(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "這是jobDetail1的trigger") .startNow()//立即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s執(zhí)行一次 .repeatForever()).build();//一直執(zhí)行
Job執(zhí)行的時(shí)候,可以獲取到這些參數(shù)信息:
@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)); }
(4)Trigger、SimpleTrigger、CronTrigger
Trigger
Trigger是Quartz的觸發(fā)器,會去通知Scheduler何時(shí)去執(zhí)行對應(yīng)Job。
- new Trigger().startAt():表示觸發(fā)器首次被觸發(fā)的時(shí)間;
- new Trigger().endAt():表示觸發(fā)器結(jié)束觸發(fā)的時(shí)間;
SimpleTrigger
SimpleTrigger可以實(shí)現(xiàn)在一個(gè)指定時(shí)間段內(nèi)執(zhí)行一次作業(yè)任務(wù)或一個(gè)時(shí)間段內(nèi)多次執(zhí)行作業(yè)任務(wù)。 下面的程序就實(shí)現(xiàn)了程序運(yùn)行5s后開始執(zhí)行Job,執(zhí)行Job 5s后結(jié)束執(zhí)行:
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執(zhí)行一次 .repeatForever()).build();//一直執(zhí)行
CronTrigger
CronTrigger功能非常強(qiáng)大,是基于日歷的作業(yè)調(diào)度,而SimpleTrigger是精準(zhǔn)指定間隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表達(dá)式的,先了解下Cron表達(dá)式: 由7個(gè)子表達(dá)式組成字符串的,格式如下:
[秒] [分] [小時(shí)] [日] [月] [周] [年]
Cron表達(dá)式的語法比較復(fù)雜, 如:* 30 10 ? * 1/5 * 表示(從后往前看) [指定年份] 的[ 周一到周五][指定月][不指定日][上午10時(shí)][30分][指定秒]
又如:00 00 00 ? * 10,11,12 1#5 2018 表示2018年10、11、12月的第一周的星期五這一天的0時(shí)0分0秒去執(zhí)行任務(wù)。
下面是給的一個(gè)例子:
可通過在線生成Cron表達(dá)式的工具:cron.qqe2.com/ 來生成自己想要的表達(dá)式。
下面的代碼就實(shí)現(xiàn)了每周一到周五上午10:30執(zhí)行定時(shí)任務(wù)
/** * Created by wanggenshen * Date: on 2018/7/7 20:06. * Description: XXX */ public class MyScheduler2 { public static void main(String[] args) throws SchedulerException, InterruptedException { // 1、創(chuàng)建調(diào)度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 2、創(chuàng)建JobDetail實(shí)例,并與PrintWordsJob類綁定(Job執(zhí)行內(nèi)容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class) .usingJobData("jobDetail1", "這個(gè)Job用來測試的") .withIdentity("job1", "group1").build(); // 3、構(gòu)建Trigger實(shí)例,每隔1s執(zhí)行一次 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(); //4、執(zhí)行 scheduler.scheduleJob(jobDetail, cronTrigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); System.out.println("--------scheduler shutdown ! ------------"); } }
到此這篇關(guān)于詳解定時(shí)任務(wù)框架Quartz的使用的文章就介紹到這了,更多相關(guān)定時(shí)任務(wù)框架Quartz內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
InputStream數(shù)據(jù)結(jié)構(gòu)示例解析
這篇文章主要為大家介紹了InputStream數(shù)據(jù)結(jié)構(gòu)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Spring 4.0新功能:@Conditional注解詳細(xì)介紹
Spring Boot的強(qiáng)大之處在于使用了Spring 4框架的新特性:@Conditional注釋,此注釋使得只有在特定條件滿足時(shí)才啟用一些配置。下面這篇文章主要給大家介紹了關(guān)于Spring4.0中新功能:@Conditional注解的相關(guān)資料,需要的朋友可以參考下。2017-09-09通過簡單步驟實(shí)現(xiàn)SpringMVC文件上傳
這篇文章主要介紹了通過簡單步驟實(shí)現(xiàn)SpringMVC文件上傳,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11