Quartz是一個(gè)開源的作業(yè)調(diào)度框架,完全由java寫的,你能通過它創(chuàng)建簡(jiǎn)單或者復(fù)雜的任務(wù)。他能提供巨大的靈活性,但又不犧牲簡(jiǎn)單性。
Quartz的核心概念 :調(diào)度器、觸發(fā)器、作業(yè)
調(diào)度器(Scheduler)
Scheduler負(fù)責(zé)管理Quartz的運(yùn)行環(huán)境,Quartz它是基于多線程架構(gòu)的,它啟動(dòng)的時(shí)候會(huì)初始化一套線程,這套線程會(huì)用來執(zhí)行一些預(yù)置的作業(yè)。
要?jiǎng)?chuàng)建一個(gè)作業(yè)并能夠被觸發(fā)調(diào)用,必須在Scheduler上面注冊(cè)一個(gè)JobDetail和Trigger。
Scheduler提攜了所有Trigger和JobDetail,使它們協(xié)調(diào)工作。這些Trigger和JobDetail通過自身的name和group屬性區(qū)分。
Scheduler 由SchedulerFactory產(chǎn)生,我們可以通過以下方式獲取Scheduler的實(shí)例:
//從ServletContext上下文中查找SchedulerFactory
SchedulerFactory factory = (SchedulerFactory)
ServletActionContext.getServletContext().getAttribute("org.quartz.impl.StdSchedulerFactory.KEY");
//獲取Scheduler對(duì)象
Scheduler defScheduler = factory.getScheduler();
作業(yè)
Job
任務(wù),其實(shí)就是一個(gè)接口。要?jiǎng)?chuàng)建一個(gè)任務(wù),必須得實(shí)現(xiàn)這個(gè)接口。該接口只有一個(gè)execute方法,任務(wù)每次被調(diào)用的時(shí)候都會(huì)執(zhí)行這個(gè)execute方法的邏輯。
public class TestJob impletemens org.quartz.Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
// you business logic
// …
System.out.println("########### this is testJob running ############");
}
}
JobDetail
JobDetail 用來保存我們作業(yè)的詳細(xì)信息。一個(gè)JobDetail可以有多個(gè)Trigger,但是一個(gè)Trigger只能對(duì)應(yīng)一個(gè)JobDetail。下面是JobDetail的一些常用的屬性和含義
參數(shù)名 |
類型 |
備注 |
name |
String |
任務(wù)的名稱,必須 |
group |
String |
任務(wù)所在組,默認(rèn)為DEFAULT |
jobClass |
Class |
任務(wù)的實(shí)現(xiàn)類,必須 |
description |
String |
描述 |
jobDataMap |
JobDataMap |
用來給作業(yè)提供數(shù)據(jù)支持的數(shù)據(jù)結(jié)構(gòu) |
volatility |
Boolean |
重啟應(yīng)用之后是否刪除任務(wù)的相關(guān)信息,默認(rèn)false |
durability |
Boolean |
任務(wù)完成之后是否依然保留到數(shù)據(jù)庫(kù),默認(rèn)false |
shouldRecover |
Boolean |
應(yīng)用重啟之后時(shí)候忽略過期任務(wù),默認(rèn)false |
jobListeners |
Set |
監(jiān)聽器 |
JobDataMap
這是一個(gè)給作業(yè)提供數(shù)據(jù)支持的數(shù)據(jù)結(jié)構(gòu),使用方法和java.util.Map一樣,非常方便。當(dāng)一個(gè)作業(yè)被分配給調(diào)度器時(shí),JobDataMap實(shí)例就隨之生成。
Job有一個(gè)StatefulJob子接口,代表有狀態(tài)的任務(wù),該接口是一個(gè)沒有方法的標(biāo)簽接口,其目的是讓Quartz知道任務(wù)的類型,以便采用不同的執(zhí)行方案。無狀態(tài)任務(wù)在執(zhí)行時(shí)擁有自己的JobDataMap拷貝,對(duì)JobDataMap的更改不會(huì)影響下次的執(zhí)行。而有狀態(tài)任務(wù)共享共享同一個(gè)JobDataMap實(shí)例,每次任務(wù)執(zhí)行對(duì)JobDataMap所做的更改會(huì)保存下來,后面的執(zhí)行可以看到這個(gè)更改,也即每次執(zhí)行任務(wù)后都會(huì)對(duì)后面的執(zhí)行發(fā)生影響。
正因?yàn)檫@個(gè)原因,無狀態(tài)的Job可以并發(fā)執(zhí)行,而有狀態(tài)的StatefulJob不能并發(fā)執(zhí)行,這意味著如果前次的StatefulJob還沒有執(zhí)行完畢,下一次的任務(wù)將阻塞等待,直到前次任務(wù)執(zhí)行完畢。有狀態(tài)任務(wù)比無狀態(tài)任務(wù)需要考慮更多的因素,程序往往擁有更高的復(fù)雜度,因此除非必要,應(yīng)該盡量使用無狀態(tài)的Job。
如果Quartz使用了數(shù)據(jù)庫(kù)持久化任務(wù)調(diào)度信息,無狀態(tài)的JobDataMap僅會(huì)在Scheduler注冊(cè)任務(wù)時(shí)保持一次,而有狀態(tài)任務(wù)對(duì)應(yīng)的JobDataMap在每次執(zhí)行任務(wù)后都會(huì)進(jìn)行保存。
JobDataMap實(shí)例也可以與一個(gè)觸發(fā)器相關(guān)聯(lián)。這種情況下,對(duì)于同一作業(yè)的不同觸發(fā)器,我們可以在JobDataMap中添加不同的數(shù)據(jù),以便作業(yè)在不同時(shí)間執(zhí)行時(shí)能夠提供更為靈活的數(shù)據(jù)支持(學(xué)校上午放眼保健操錄音第一版,下午放第二版)。
不管是有狀態(tài)還是無狀態(tài)的任務(wù),在任務(wù)執(zhí)行期間對(duì)Trigger的JobDataMap所做的更改都不會(huì)進(jìn)行持久,也即不會(huì)對(duì)下次的執(zhí)行產(chǎn)生影響。