詳解定時任務框架Quartz的使用
一、什么是Quartz
什么是Quartz?
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,完全由Java開發(fā),可以用來執(zhí)行定時任務,類似于java.util.Timer。但是相較于Timer, Quartz增加了很多功能:
- 持久性作業(yè) – 就是保持調度定時的狀態(tài);
- 作業(yè)管理 – 對調度作業(yè)進行有效的管理;
大部分公司都會用到定時任務這個功能。 拿火車票購票來說,當你下單后,后臺就會插入一條待支付的task(job),一般是30分鐘,超過30min后就會執(zhí)行這個job,去判斷你是否支付,未支付就會取消此次訂單;當你支付完成之后,后臺拿到支付回調后就會再插入一條待消費的task(job),Job觸發(fā)日期為火車票上的出發(fā)日期,超過這個時間就會執(zhí)行這個job,判斷是否使用等。
在我們實際的項目中,當Job過多的時候,肯定不能人工去操作,這時候就需要一個任務調度框架,幫我們自動去執(zhí)行這些程序。那么該如何實現(xiàn)這個功能呢?
(1)首先我們需要定義實現(xiàn)一個定時功能的接口,我們可以稱之為Task(或Job),如定時發(fā)送郵件的task(Job),重啟機器的task(Job),優(yōu)惠券到期發(fā)送短信提醒的task(Job),實現(xiàn)接口如下:

(2)有了任務之后,還需要一個能夠實現(xiàn)觸發(fā)任務去執(zhí)行的觸發(fā)器,觸發(fā)器Trigger最基本的功能是指定Job的執(zhí)行時間,執(zhí)行間隔,運行次數(shù)等。

(3)有了Job和Trigger后,怎么樣將兩者結合起來呢?即怎樣指定Trigger去執(zhí)行指定的Job呢?這時需要一個Schedule,來負責這個功能的實現(xiàn)。

上面三個部分就是Quartz的基本組成部分:
- 調度器:Scheduler
- 任務:JobDetail
- 觸發(fā)器:Trigger,包括SimpleTrigger和CronTrigger
二、Quartz Demo搭建
下面來利用Quartz搭建一個最基本的Demo。
1、導入依賴的jar包:
org.quartz-scheduler quartz 2.3.0
2、新建一個能夠打印任意內容的Job:
/** * Created by wanggenshen * Date: on 2018/7/7 16:28. * Description: 打印任意內容 */
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í)行任務:
/** * 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)建調度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2、創(chuàng)建JobDetail實例,并與PrintWordsJob類綁定(Job執(zhí)行內容)
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
.withIdentity("job1", "group1").build();
// 3、構建Trigger實例,每隔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 ! ------------");
}
}運行程序,可以看到程序每隔1s會打印出內容,且在一分鐘后結束:

三、Quartz核心詳解
下面就程序中出現(xiàn)的幾個參數(shù),看一下Quartz框架中的幾個重要參數(shù):
- Job和JobDetail
- JobExecutionContext
- JobDataMap
- Trigger、SimpleTrigger、CronTrigger
(1)Job和JobDetail Job是Quartz中的一個接口,接口下只有execute方法,在這個方法中編寫業(yè)務邏輯。 接口中的源碼:

JobDetail用來綁定Job,為Job實例提供許多屬性:
- name
- group
- jobClass
- jobDataMap
JobDetail綁定指定的Job,每次Scheduler調度執(zhí)行一個Job的時候,首先會拿到對應的Job,然后創(chuàng)建該Job實例,再去執(zhí)行Job中的execute()的內容,任務執(zhí)行結束后,關聯(lián)的Job對象實例會被釋放,且會被JVM GC清除。
為什么設計成JobDetail + Job,不直接使用Job
JobDetail定義的是任務數(shù)據(jù),而真正的執(zhí)行邏輯是在Job中。 這是因為任務是有可能并發(fā)執(zhí)行,如果Scheduler直接使用Job,就會存在對同一個Job實例并發(fā)訪問的問題。而JobDetail & Job 方式,Sheduler每次執(zhí)行,都會根據(jù)JobDetail創(chuàng)建一個新的Job實例,這樣就可以規(guī)避并發(fā)訪問的問題。
(2)JobExecutionContext
JobExecutionContext中包含了Quartz運行時的環(huán)境以及Job本身的詳細數(shù)據(jù)信息。 當Schedule調度執(zhí)行一個Job的時候,就會將JobExecutionContext傳遞給該Job的execute()中,Job就可以通過JobExecutionContext對象獲取信息。 主要信息有:

(3)JobExecutionContext JobDataMap實現(xiàn)了JDK的Map接口,可以以Key-Value的形式存儲數(shù)據(jù)。 JobDetail、Trigger都可以使用JobDataMap來設置一些參數(shù)或信息, Job執(zhí)行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執(zhí)行一次
.repeatForever()).build();//一直執(zhí)行
Job執(zhí)行的時候,可以獲取到這些參數(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何時去執(zhí)行對應Job。
- new Trigger().startAt():表示觸發(fā)器首次被觸發(fā)的時間;
- new Trigger().endAt():表示觸發(fā)器結束觸發(fā)的時間;
SimpleTrigger
SimpleTrigger可以實現(xiàn)在一個指定時間段內執(zhí)行一次作業(yè)任務或一個時間段內多次執(zhí)行作業(yè)任務。 下面的程序就實現(xiàn)了程序運行5s后開始執(zhí)行Job,執(zhí)行Job 5s后結束執(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功能非常強大,是基于日歷的作業(yè)調度,而SimpleTrigger是精準指定間隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表達式的,先了解下Cron表達式: 由7個子表達式組成字符串的,格式如下:
[秒] [分] [小時] [日] [月] [周] [年]
Cron表達式的語法比較復雜, 如:* 30 10 ? * 1/5 * 表示(從后往前看) [指定年份] 的[ 周一到周五][指定月][不指定日][上午10時][30分][指定秒]
又如:00 00 00 ? * 10,11,12 1#5 2018 表示2018年10、11、12月的第一周的星期五這一天的0時0分0秒去執(zhí)行任務。
下面是給的一個例子:

可通過在線生成Cron表達式的工具:cron.qqe2.com/ 來生成自己想要的表達式。

下面的代碼就實現(xiàn)了每周一到周五上午10:30執(zhí)行定時任務
/** * 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)建調度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2、創(chuàng)建JobDetail實例,并與PrintWordsJob類綁定(Job執(zhí)行內容)
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
.usingJobData("jobDetail1", "這個Job用來測試的")
.withIdentity("job1", "group1").build();
// 3、構建Trigger實例,每隔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 ! ------------");
}
}到此這篇關于詳解定時任務框架Quartz的使用的文章就介紹到這了,更多相關定時任務框架Quartz內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
maven+阿里云創(chuàng)建國內鏡像的中央倉庫(親測可用)
本篇文章主要介紹了maven+阿里云創(chuàng)建國內鏡像的中央倉庫(親測可用),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
如何使用Spring Security實現(xiàn)用戶-角色-資源的權限控制
文章介紹了如何通過SpringSecurity實現(xiàn)用戶-角色-資源的權限管理,包括基于角色的請求控制、加載用戶角色信息、角色與資源的關聯(lián)等步驟,同時,提供了一些測試場景,以驗證權限控制是否正確,感興趣的朋友跟隨小編一起看看吧2024-10-10
Mybatis的parameterType造成線程阻塞問題分析
這篇文章主要詳細分析了Mybatis的parameterType造成線程阻塞問題,文中有詳細的解決方法,及相關的代碼示例,具有一定的參考價值,感興趣的朋友可以借鑒閱讀2023-06-06
解決java maven項目找不到jconsole-1.8.0.jar和tools-1.8.0.jar包問題
這篇文章主要介紹了解決java maven項目找不到jconsole-1.8.0.jar和tools-1.8.0.jar包問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
在 Spring Boot 中使用異步線程時的 HttpServletReque
文章討論了在SpringBoot中使用異步線程時,由于HttpServletRequest復用導致的Cookie解析失敗問題,為了解決這個問題,文章推薦了使用HttpServletRequestWrapper創(chuàng)建請求副本、手動傳遞請求上下文和延遲請求清理等方法,感興趣的朋友一起看看吧2025-03-03

