Quartz 是一個完全由 Java 編寫的開源作業調度框架,不要讓作業調度這個術語嚇着你,其實不難。盡管 Quartz 框架整合了許多額外功能,但就我們使用來說,你會發現它易用得簡直讓人受不了!
簡單來說,任務調度就是在指定時間做指定的事,之前說過在執行定時定頻率作業時可以使用原生 JDK,Timer 和 TimerTask 。
但是假如我們有一些非常苛刻的要求該怎么辦呢?比如,在每年 5 月的第二個星期日和每年 6 月的第三個星期日給我發一個郵件。
這種看似隨機但還有有一點規律的定時任務該怎么實現呢?別急,Quartz 都能給你搞定!
我們執行任務必須的幾個條件,一個是調度器,二是執行的任務,三是觸發器(可以理解為設置鬧鍾)。連在一起就是我們使用調度器將任務和觸發器綁定在一起執行,以達到在指定的時間點執行或循環執行任務。
雖說 Quartz 框架有十多個包,300 多個類,但是我們使用它還是比較容易的。
首先,創建一個任務,一樣的套路我們只需要實現 Job 類,實現特定的方法即可,任務創建成功。
public class HelloJob implements Job{ @Override public void execute(JobExecutionContext context) throws JobExecutionException { TimeUtil.printCurrentTime(); System.out.println("Hello world!"); } }
但是我們調度器綁定的不是 Job 的實現類 HelloJob,而是一個 JobDetail,為什么需要它呢,都寫在臉上了,Job 詳情嘛!
我們執行任務需要知道任務的名稱啊,分組之類的,這樣打出來的 log 也好分辨。這個 JobDetail 是和哪個 Job 綁定的呢?我們還要知道 Class 信息。在任務執行的時候可能還需要傳參,這些都會封裝在 JobDetail 中。
JobDetail 的創建就更是巧妙了,Quartz 為任務和觸發器的創建提供了一種構建器風格的 API。具體的創建就是這樣(一路點下去)
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
呃呃,構建器風格…… 我也不是很懂,感覺用起來很方便就對了,這樣我們就創建了一個 JobDetail ,並設置了綁定的 Job (HelloJob),還為任務命名並分組,其實還可以繼續綁定參數。這里沒有演示而已。
同樣的創建風格也用在觸發器身上,想想看我們要設置一個鬧鍾,我們要設置開始執行的時間,執行的頻率,執行的次數,這些一次性搞定,若是你英文還可以,看這些方法應該都明白了。
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
哎,不是說好了不按套路出牌的嘛,我要的是每年 5 月的第二個星期日執行呀,可不是定時定頻率啊,別急,后面說。
最后,再創建一個調度器,使用工廠模式創建,這里選擇 StdSchedulerFactory ,因為這個工廠是可以加載配置文件的。在框架中會默認給我們提供一套配置,我們也可以在項目根目錄下自行創建。
調度器的創建、綁定任務和觸發器到執行,幾行代碼搞定。
// 定義一個 Schedule,用於綁定任務和觸發器 SchedulerFactory sf = new StdSchedulerFactory(); Scheduler scheduler = sf.getScheduler(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start();
好了,這里為此,我們就已經搞定了最牛逼的定時任務的 demo 了。但是,你有沒有發現,若是想實現定時定頻率的執行任務,使用 Timer 就可以啊,為什么要勞煩大哥出場呢?
這里提一下 Timer 和 Quartz 的區別吧。
1、Timer 只能執行定時定頻率的任務,而 Quartz 不是。
2、Timer 只有一個線程在執行,而 Quartz 有線程池,默認開啟 10 個線程。
3、Timer 中出現異常,一切 GG,不能記錄事故現場,而 Quartz 可以。
Quartz 想靈活的設置執行時間,依賴誰呢?調度器?NO,那肯定是觸發器啊,其實 Trigger 有兩個實現類,一個是 SimpleTrigger 就是功能比較簡單的那個,默認實現的就是這個,所以上面我們返回就是 SimpleTrigger 的對象。
而另一個重磅級的實現類就是 CronTrigger ,它的觸發機制是基於日歷的,所以,你能想到的每一天,都能給你表示出來,觸發器,重點就在這個時間的表示上面,我們使用 Cron 表達式來表示執行的時間。
舉個例子吧 "* * * * * ? *" 表示每秒鍾都執行,"0 15 10 ? 6L 2018-2020" 表示 2018-2020 年的每個月的最后一個星期五上午 10:15 執行。你會想,WTF ? 這是什么鬼,我不打算細說這個 Cron 表達式怎么寫,因為不需要自己寫!簡單說,這就類似與正則表達式,而我們可以用在線生成工具來生成他們。
你只需要搜索 Cron 表達式在線生成器即可。
有了隨心所欲的時間之后,我們就可以使用 CronTrigger 來定義觸發器了,核心就在 Cron 表達式上面。
CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity("trigger1", "group").startNow() .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *")).build();
至於多線程和持久化相關的類都在調度器中,不得不說 Quartz 封裝的就是好呀,使用者根本感覺不到這些,在 StdSchedulerFactory 的內部,封裝了核心調度器 QuartzScheduler ,用於持久化任務的 JobStore ,多線程相關的線程池、線程執行器。
但是我這種沒有具體業務需求的人是不會深入研究的,對了,不要忘了 Quartz 的可配置的特性,簡直就是逆天,沒有業務場景應用的我也就想想得了。
以上也就是帶你了解一下 Quartz,自己玩玩完全沒問題。但是,你要是還不知道 Timer ,那就是你的問題了,可以回頭看看這篇。