Quartz 入门详解
入門簡介:
基本上任何公司都會用到調度這個功能, 比如我們公司需要定期執行調度生成報表, 或者比如博客什么的定時更新之類的,都可以靠Quartz來完成。正如官網所說,小到獨立應用大到大型電子商務網站, Quartz都能勝任。
Quartz體系結構:
明白Quartz怎么用,首先要了解Scheduler(調度器)、Job(任務)和Trigger(觸發器)這3個核心的概念。
1. Job:?是一個接口,只定義一個方法execute(JobExecutionContext context),在實現接口的execute方法中編寫所需要定時執行的Job(任務), JobExecutionContext類提供了調度應用的一些信息。Job運行時的信息保存在JobDataMap實例中;
2. JobDetail:?Quartz每次調度Job時, 都重新創建一個Job實例, 所以它不直接接受一個Job的實例,相反它接收一個Job實現類(JobDetail:描述Job的實現類及其它相關的靜態信息,如Job名字、描述、關聯監聽器等信息),以便運行時通過newInstance()的反射機制實例化Job。
3. Trigger:?是一個類,描述觸發Job執行的時間觸發規則。主要有SimpleTrigger和CronTrigger這兩個子類。當且僅當需調度一次或者以固定時間間隔周期執行調度,SimpleTrigger是最適合的選擇;而CronTrigger則可以通過Cron表達式定義出各種復雜時間規則的調度方案:如工作日周一到周五的15:00~16:00執行調度等;
Cron表達式的格式:秒 分 時 日 月 周 年(可選)。
字段名 ? ? ? ? ? ? ? ? 允許的值 ? ? ? ? ? ? ? ? ? ? ? ?允許的特殊字符
秒 ? ? ? ? ? ? ? ? ? ? ? ? 0-59 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? , – * /
分 ? ? ? ? ? ? ? ? ? ? ? ? 0-59 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? , – * /
小時 ? ? ? ? ? ? ? ? ? 0-23 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? , – * /
日 ? ? ? ? ? ? ? ? ? ? ? ? 1-31 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? , – * ? / L W C
月 ? ? ? ? ? ? ? ? ? ? ? ? 1-12 or JAN-DEC ? ? ? ? ? , – * /
周幾 ? ? ? ? ? ? ? ? ? ? 1-7 or SUN-SAT ? ? ? ? ? ? , – * ? / L C # ? ? ?MON ?FRI
年 (可選字段) ? ? empty, 1970-2099 ? ? ? ? ? ?, – * /
“?”字符:表示不確定的值
“,”字符:指定數個值
“-”字符:指定一個值的范圍
“/”字符:指定一個值的增加幅度。n/m表示從n開始,每次增加m
“L”字符:用在日表示一個月中的最后一天,用在周表示該月最后一個星期X
“W”字符:指定離給定日期最近的工作日(周一到周五)
“#”字符:表示該月第幾個周X。6#3表示該月第3個周五
Cron表達式范例:
每隔5秒執行一次:*/5 * * * * ?
每隔1分鐘執行一次:0 */1 * * * ?
每天23點執行一次:0 0 23 * * ?
每天凌晨1點執行一次:0 0 1 * * ?
每月1號凌晨1點執行一次:0 0 1 1 * ?
每月最后一天23點執行一次:0 0 23 L * ?
每周星期天凌晨1點實行一次:0 0 1 ? * L
在26分、29分、33分執行一次:0 26,29,33 * * * ?
每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ?
4. Calendar:org.quartz.Calendar和java.util.Calendar不同, 它是一些日歷特定時間點的集合(可以簡單地將org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一個日歷時間點,無特殊說明后面的Calendar即指org.quartz.Calendar)。 一個Trigger可以和多個Calendar關聯, 以便排除或包含某些時間點。
假設,我們安排每周星期一早上10:00執行任務,但是如果碰到法定的節日,任務則不執行,這時就需要在Trigger觸發機制的基礎上使用Calendar進行定點排除。針對不同時間段類型,Quartz在org.quartz.impl.calendar包下提供了若干個Calendar的實現類,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分別針對每年、每月和每周進行定義;
5. Scheduler:?代表一個Quartz的獨立運行容器, Trigger和JobDetail可以注冊到Scheduler中, 兩者在Scheduler中擁有各自的組及名稱, 組及名稱是Scheduler查找定位容器中某一對象的依據, Trigger的組及名稱必須唯一, JobDetail的組和名稱也必須唯一(但可以和Trigger的組和名稱相同,因為它們是不同類型的)。Scheduler定義了多個接口方法, 允許外部通過組及名稱訪問和控制容器中Trigger和JobDetail。
Scheduler可以將Trigger綁定到某一JobDetail中, 這樣當Trigger觸發時, 對應的Job就被執行。一個Job可以對應多個Trigger, 但一個Trigger只能對應一個Job。可以通過SchedulerFactory創建一個Scheduler實例。Scheduler擁有一個SchedulerContext,它類似于ServletContext,保存著Scheduler上下文信息,Job和Trigger都可以訪問SchedulerContext內的信息。SchedulerContext內部通過一個Map,以鍵值對的方式維護這些上下文數據,SchedulerContext為保存和獲取數據提供了多個put()和getXxx()的方法。可以通過Scheduler# getContext()獲取對應的SchedulerContext實例;
6. ThreadPool:?Scheduler使用一個線程池作為任務運行的基礎設施,任務通過共享線程池中的線程提高運行效率。
Job有一個StatefulJob子接口,代表有狀態的任務,該接口是一個沒有方法的標簽接口,其目的是讓Quartz知道任務的類型,以便采用不同的執行方案。無狀態任務在執行時擁有自己的JobDataMap拷貝,對JobDataMap的更改不會影響下次的執行。而有狀態任務共享共享同一個JobDataMap實例,每次任務執行對JobDataMap所做的更改會保存下來,后面的執行可以看到這個更改,也即每次執行任務后都會對后面的執行發生影響。
正因為這個原因,無狀態的Job可以并發執行,而有狀態的StatefulJob不能并發執行,這意味著如果前次的StatefulJob還沒有執行完畢,下一次的任務將阻塞等待,直到前次任務執行完畢。有狀態任務比無狀態任務需要考慮更多的因素,程序往往擁有更高的復雜度,因此除非必要,應該盡量使用無狀態的Job。
如果Quartz使用了數據庫持久化任務調度信息,無狀態的JobDataMap僅會在Scheduler注冊任務時保持一次,而有狀態任務對應的JobDataMap在每次執行任務后都會進行保存。
Trigger自身也可以擁有一個JobDataMap,其關聯的Job可以通過JobExecutionContext#getTrigger().getJobDataMap()獲取Trigger中的JobDataMap。不管是有狀態還是無狀態的任務,在任務執行期間對Trigger的JobDataMap所做的更改都不會進行持久,也即不會對下次的執行產生影響。
Quartz擁有完善的事件和監聽體系,大部分組件都擁有事件,如任務執行前事件、任務執行后事件、觸發器觸發前事件、觸發后事件、調度器開始事件、關閉事件等等,可以注冊相應的監聽器處理感興趣的事件。
下圖描述了Scheduler的內部組件結構,SchedulerContext提供Scheduler全局可見的上下文信息,每一個任務都對應一個JobDataMap,虛線表達的JobDataMap表示對應有狀態的任務:
廢話不多說, 上代碼:
1. 最簡單的Job代碼(就打印Hello Quartz !):
| 1 2 3 4 5 6 7 8 9 10 11 12 | packagecom.wenniuwuren.quartz;? importorg.quartz.Job;? importorg.quartz.JobExecutionContext;? importorg.quartz.JobExecutionException;? publicclassHelloQuartz? implementsJob {? ????publicvoidexecute(JobExecutionContext arg0) throwsJobExecutionException {? ????????System.out.println("Hello Quartz !");???????????????? ????}???????? } |
2. 設置觸發器
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | packagecom.wenniuwuren.quartz;? importorg.quartz.CronScheduleBuilder;??? importorg.quartz.JobBuilder;??? importorg.quartz.JobDetail;??? importorg.quartz.Scheduler;??? importorg.quartz.SchedulerException;? importorg.quartz.SchedulerFactory;??? importorg.quartz.SimpleScheduleBuilder;? importorg.quartz.Trigger;??? importorg.quartz.TriggerBuilder;??? importorg.quartz.impl.StdSchedulerFactory;??? publicclassSchedulerTest {??? ???publicstaticvoid main(String[] args) throwsInterruptedException {??? ???????//通過schedulerFactory獲取一個調度器??? ???????SchedulerFactory schedulerfactory = newStdSchedulerFactory();??? ???????Scheduler scheduler=null;??? ???????try{??? ???????????// 通過schedulerFactory獲取一個調度器??? ???????????scheduler = schedulerfactory.getScheduler();??? ????????????// 創建jobDetail實例,綁定Job實現類??? ????????????// 指明job的名稱,所在組的名稱,以及綁定job類??? ???????????JobDetail job = JobBuilder.newJob(HelloQuartz.class).withIdentity("JobName","JobGroupName").build();??? ????????????// 定義調度觸發規則??? ????????????// SimpleTrigger?? //????? Trigger trigger=TriggerBuilder.newTrigger().withIdentity("SimpleTrigger", "SimpleTriggerGroup")??? //??????????????????? .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3).withRepeatCount(6))??? //??????????????????? .startNow().build();??? ????????????//? corn表達式? 每五秒執行一次? ??????????????Trigger trigger=TriggerBuilder.newTrigger().withIdentity("CronTrigger1","CronTriggerGroup")??? ??????????????.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))??? ??????????????.startNow().build();???? ????????????// 把作業和觸發器注冊到任務調度中??? ???????????scheduler.scheduleJob(job, trigger);??? ???????????// 啟動調度??? ???????????scheduler.start();??? ???????????Thread.sleep(10000);? ???????????// 停止調度? ???????????scheduler.shutdown();? ???????}catch(SchedulerException e){??? ???????????e.printStackTrace();??? ???????}??? ???}??? } |
輸出(設置了sleep10秒, 故在0秒調度一次, 5秒一次, 10秒最后一次):
from:?http://www.importnew.com/22890.html 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的Quartz 入门详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是Java对象分配率?
- 下一篇: 可以落地的软件架构