Quartz任務調度--入門

Quartz對任務調度的領域問題進行了高度的抽象,提出了調度器、任務和觸發器這3個核心的概念,並在org.quartz通過接口和類對重要的這些核心概念進行描述:

●Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中;

●JobDetail:Quartz在每次執行Job時,都重新創建一個Job實例,所以它不直接接受一個Job的實例,相反它接收一個Job實現類,以便運行時通過newInstance()的反射機制實例化Job。因此需要通過一個類來描述Job的實現類及其它相關的靜態信息,如Job名字、描述、關聯監聽器等信息,JobDetail承擔了這一角色。

通過該類的構造函數可以更具體地瞭解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),該構造函數要求指定Job的實現類,以及任務在Scheduler中的組名和Job名稱;

●Trigger:是一個類,描述觸發Job執行的時間觸發規則。主要有SimpleTrigger和CronTrigger這兩個子類。當僅需觸發一次或者以固定時間間隔週期執行,SimpleTrigger是最適合的選擇;而CronTrigger則可以通過Cron表達式定義出各種複雜時間規則的調度方案:如每早晨9:00執行,週一、週三、週五下午5:00執行等;

●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分別針對每年、每月和每週進行定義;

●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實例;

●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擁有完善的事件和監聽體系,大部分組件都擁有事件,如任務執行前事件、任務執行後事件、觸發器觸發前事件、觸發後事件、調度器開始事件、關閉事件等等,可以註冊相應的監聽器處理感興趣的事件。

 

下面寫個例子,每隔1秒控制檯打印一條信息。
1、Create a Java project,目錄結構如下:

2、引入相關jar

3、HelloWorldJob類,定時任務類

複製代碼

package com.ljq.quartz;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.Trigger;

/**
 * 定時任務類
 * 
 * @author 林計欽
 * @version 1.0 2013-7-9 上午09:19:52
 */
public class HelloWorldJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        //執行定時器任務
        Trigger trigger = context.getTrigger();
        Scheduler scheduler=context.getScheduler();
        System.out.println(String.format("hello world at %s, trigger[%s - %s]", 
                date(), trigger.getGroup(), trigger.getName()));
        
    }
    
    private String date(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(new Date());
    }

}

複製代碼

4、 HelloWorldJobTest測試類

複製代碼

package com.ljq.quartz;

import java.text.ParseException;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

/**
 * Quartz測試類
 * 
 * @author 林計欽
 * @version 1.0 2013-7-9 上午09:22:43
 */
public class HelloWorldJobTest {
    public static void main(String[] args) throws SchedulerException, ParseException {
        JobDetail job = new JobDetail("helloWorldJob", Scheduler.DEFAULT_GROUP, HelloWorldJob.class);
    
        //Trigger trigger = TriggerUtils.makeSecondlyTrigger(1);
        //trigger.setName("helloWorldTrigger");
        //trigger.setStartTime(new Date());
        CronTrigger trigger = new CronTrigger("helloWorldTrigger", "cronGroup", "1/1 * * * * ?");  
        
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        scheduler.scheduleJob(job, trigger);
        //scheduler.shutdown();
    }
}

複製代碼

5、運行結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章