天天看點

Java定時任務排程詳解

在實際項目開發中,除了Web應用、SOA服務外,還有一類不可缺少的,那就是定時任務排程。定時任務的場景可以說非常廣泛,比如某些視訊網站,購買會員後,每天會給會員送成長值,每月會給會員送一些電影券;比如在保證最終一緻性的場景中,往往利用定時任務排程進行一些比對工作;比如一些定時需要生成的報表、郵件;比如一些需要定時清理資料的任務等。本篇部落格将系統的介紹定時任務排程,會涵蓋Timer、ScheduledExecutorService、開源工具包Quartz,以及Spring和Quartz的結合等内容。

定時任務排程:基于給定的時間點、給定的時間間隔、給定的執行次數自動執行的任務。 Timer位于java.util包下,其内部包含且僅包含一個背景線程(TimeThread)對多個業務任務(TimeTask)進行定時定頻率的排程。

schedule的四種用法和scheduleAtFixedRate的兩種用法

Java定時任務排程詳解
參數說明: task:所要執行的任務,需要extends TimeTask override run() time/firstTime:首次執行任務的時間 period:周期性執行Task的時間間隔,機關是毫秒 delay:執行task任務前的延時時間,機關是毫秒 很顯然,通過上述的描述,我們可以實作: 延遲多久後執行一次任務;指定時間執行一次任務;延遲一段時間,并周期性執行任務;指定時間,并周期性執行任務;

思考1:如果time/firstTime指定的時間,在目前時間之前,會發生什麼呢?

在時間等于或者超過time/firstTime的時候,會執行task!也就是說,如果time/firstTime指定的時間在目前時間之前,就會立即得到執行。

思考2:schedule和scheduleAtFixedRate有什麼差別?

scheduleAtFixedRate:每次執行時間為上一次任務開始起向後推一個period間隔,也就是說下次執行時間相對于上一次任務開始的時間點,是以執行時間不會延後,但是存在任務并發執行的問題。 schedule:每次執行時間為上一次任務結束後推一個period間隔,也就是說下次執行時間相對于上一次任務結束的時間點,是以執行時間會不斷延後。

思考3:如果執行task發生異常,是否會影響其他task的定時排程?

如果TimeTask抛出RuntimeException,那麼Timer會停止所有任務的運作!

思考4:Timer的一些缺陷?

前面已經提及到Timer背後是一個單線程,是以Timer存在管理并發任務的缺陷:所有任務都是由同一個線程來排程,所有任務都是串行執行,意味着同一時間隻能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之後的任務。 其次,Timer的一些排程方式還算比較簡單,無法适應實際項目中任務定時排程的複雜度。

一個簡單的Demo執行個體

Java定時任務排程詳解
Java定時任務排程詳解

Timer其他需要關注的方法

cancel():終止Timer計時器,丢棄所有目前已安排的任務(TimeTask也存在cancel()方法,不過終止的是TimeTask) purge():從計時器的任務隊列中移除已取消的任務,并傳回個數
由于Timer存在的問題,JDK5之後便提供了基于線程池的定時任務排程:ScheduledExecutorService。 設計理念:每一個被排程的任務都會被線程池中的一個線程去執行,是以任務可以并發執行,而且互相之間不受影響。

我們直接看例子:

Java定時任務排程詳解
Java定時任務排程詳解
雖然ScheduledExecutorService對Timer進行了線程池的改進,但是依然無法滿足複雜的定時任務排程場景。是以OpenSymphony提供了強大的開源任務排程架構:Quartz。Quartz是純Java實作,而且作為Spring的預設排程架構,由于Quartz的強大的排程功能、靈活的使用方式、還具有分布式叢集能力,可以說Quartz出馬,可以搞定一切定時任務排程!

Quartz的體系結構

Java定時任務排程詳解

先來看一個Demo:

Java定時任務排程詳解
Java定時任務排程詳解
說明: 1、從代碼上來看,有XxxBuilder、XxxFactory,說明Quartz用到了Builder、Factory模式,還有非常易懂的鍊式程式設計風格。 2、Quartz有3個核心概念:排程器(Scheduler)、任務(Job&JobDetail)、觸發器(Trigger)。(一個任務可以被多個觸發器觸發,一個觸發器隻能觸發一個任務) 3、注意當Scheduler排程Job時,實際上會通過反射newInstance一個新的Job執行個體(待排程完畢後銷毀掉),同時會把JobExecutionContext傳遞給Job的execute方法,Job執行個體通過JobExecutionContext通路到Quartz運作時的環境以及Job本身的明細資料。 4、JobDataMap可以裝載任何可以序列化的資料,存取很友善。需要注意的是JobDetail和Trigger都可以各自關聯上JobDataMap。JobDataMap除了可以通過上述代碼擷取外,還可以在YourJob實作類中,添加相應setter方法擷取。 5、Trigger用來告訴Quartz排程程式什麼時候執行,常用的觸發器有2種:SimpleTrigger(類似于Timer)、CronTrigger(類似于Linux的Crontab)。 6、實際上,Quartz在進行排程器初始化的時候,會加載quartz.properties檔案進行一些屬性的設定,比如Quartz背景線程池的屬性(threadCount)、作業存儲設定等。它會先從工程中找,如果找不到那麼就是用quartz.jar中的預設的quartz.properties檔案。 7、Quartz存在監聽器的概念,比如任務執行前後、任務的添加等,可以友善實作任務的監控。

CronTrigger示例

Java定時任務排程詳解
Java定時任務排程詳解
Java定時任務排程詳解
這裡給出一些常用的示例: 0 15 10 ? * 每天10點15分觸發 0 15 10 ? 2017 2017年每天10點15分觸發 0 14 * ? 每天下午的 2點到2點59分每分觸發 0 0/5 14 ? 每天下午的 2點到2點59分(整點開始,每隔5分觸發) 0 0/5 14,18 ? 每天下午的 2點到2點59分、18點到18點59分(整點開始,每隔5分觸發) 0 0-5 14 ? 每天下午的 2點到2點05分每分觸發 0 15 10 ? * 6L 每月最後一周的星期五的10點15分觸發 0 15 10 ? * 6#3 每月的第三周的星期五開始觸發 我們可以通過一些Cron線上工具非常友善的生成,比如http://www.pppet.net/等。
實際上,Quartz和Spring結合是很友善的,無非就是進行一些配置。大概基于2種方式: 第一,普通的類,普通的方法,直接在配置中指定(MethodInvokingJobDetailFactoryBean)。 第二,需要繼承QuartzJobBean,複寫指定方法(executeInternal)即可。 然後,就是一些觸發器、排程器的配置了,這裡不再展開介紹了,隻要弄懂了原生的Quartz的使用,那麼和Spring的結合使用就會很簡單。 本文轉自zfz_linux_boy 51CTO部落格,原文連結:http://blog.51cto.com/zhangfengzhe/2064092,如需轉載請自行聯系原作者