幾種任務調度的 Java 實現方法與比較2——閱讀

 Quartzjava

    Quartz 能夠知足更多更復雜的調度需求,首先讓咱們看看如何用 Quartz 實現每星期二 16:38 的調度安排:併發

清單1:ide

package com.ibm.scheduler;
import java.util.Date;

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.helpers.TriggerUtils;

public class QuartzTest implements Job {

    @Override
    //該方法實現須要執行的任務
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("Generating report - "
                + arg0.getJobDetail().getFullName() + ", type ="
                + arg0.getJobDetail().getJobDataMap().get("type"));
        System.out.println(new Date().toString());
    }
    public static void main(String[] args) {
        try {
            // 建立一個Scheduler
            SchedulerFactory schedFact = 
            new org.quartz.impl.StdSchedulerFactory();
            Scheduler sched = schedFact.getScheduler();
            sched.start();
            // 建立一個JobDetail,指明name,groupname,以及具體的Job類名,
            //該Job負責定義須要執行任務
            JobDetail jobDetail = new JobDetail("myJob", "myJobGroup",
                    QuartzTest.class);
            jobDetail.getJobDataMap().put("type", "FULL");
            // 建立一個每週觸發的Trigger,指明星期幾幾點幾分執行
            Trigger trigger = TriggerUtils.makeWeeklyTrigger(3, 16, 38);
            trigger.setGroup("myTriggerGroup");
            // 從當前時間的下一秒開始執行
            trigger.setStartTime(TriggerUtils.getEvenSecondDate(new Date()));
            // 指明trigger的name
            trigger.setName("myTrigger");
            // 用scheduler將JobDetail與Trigger關聯在一塊兒,開始調度任務
            sched.scheduleJob(jobDetail, trigger);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

    輸出結果:函數

/**
輸出結果:
Generating report - myJobGroup.myJob, type =FULL
Tue Feb 8 16:38:00 CST 2011
Generating report - myJobGroup.myJob, type =FUL
Tue Feb 15 16:38:00 CST 2011
*/

        清單 1很是簡潔地實現了一個上述複雜的任務調度。Quartz 設計的核心類包括 Scheduler, Job 以及 Trigger。其中,Job 負責定義須要執行的任務,Trigger 負責設置調度策略,Scheduler 將兩者組裝在一塊兒,並觸發任務開始執行。測試


Jobspa

       使用者只須要建立一個 Job 的繼承類,實現 execute 方法。JobDetail 負責封裝 Job 以及 Job 的屬性,並將其提供給 Scheduler 做爲參數。每次 Scheduler 執行任務時,首先會建立一個 Job 的實例,而後再調用 execute 方法執行。Quartz 沒有爲 Job 設計帶參數的構造函數,所以須要經過額外的 JobDataMap 來存儲 Job 的屬性。JobDataMap 能夠存儲任意數量的 Key,Value 對,例如: 設計

jobDetail.getJobDataMap().put("myDescription", "my job description"); 
jobDetail.getJobDataMap().put("myValue", 1998); 
ArrayList<String> list = new ArrayList<String>(); 
list.add("item1"); 
jobDetail.getJobDataMap().put("myArray", list);

JobDataMap 中的數據能夠經過下面的方式獲取:code

public class JobDataMapTest implements Job {

    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        //從context中獲取instName,groupName以及dataMap
        String instName = context.getJobDetail().getName();
        String groupName = context.getJobDetail().getGroup();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        //從dataMap中獲取myDescription,myValue以及myArray
        String myDescription = dataMap.getString("myDescription");
        int myValue = dataMap.getInt("myValue");
        ArrayList<String> myArray = (ArrayListlt;Strin>) dataMap.get("myArray");
        System.out.println("
                Instance =" + instName + ", group = " + groupName
                + ", description = " + myDescription + ", value =" + myValue
                + ", array item0 = " + myArray.get(0));

    }
}

輸出結果: orm

/**
輸出結果:
Instance = myJob, group = myJobGroup, 
description = my job description, 
value =1998, array item0 = item1
*/


Trigger繼承

    Trigger 的做用是設置調度策略。Quartz 設計了多種類型的 Trigger,其中最經常使用的是 SimpleTrigger 和 CronTrigger。

    SimpleTrigger 適用於在某一特定的時間執行一次,或者在某一特定的時間以某一特定時間間隔執行屢次。上述功能決定了 SimpleTrigger 的參數包括 start-time, end-time, repeat count, 以及 repeat interval。

    Repeat count 取值爲大於或等於零的整數,或者常量 SimpleTrigger.REPEAT_INDEFINITELY。

    Repeat interval 取值爲大於或等於零的長整型。當 Repeat interval 取值爲零而且 Repeat count 取值大於零時,將會觸發任務的併發執行。

    Start-time 與 dnd-time 取值爲 java.util.Date。當同時指定 end-time 與 repeat count 時,優先考慮 end-time。通常地,能夠指定 end-time,並設定 repeat count 爲 REPEAT_INDEFINITELY。

如下是 SimpleTrigger 的構造方法:

public SimpleTrigger(String name, 
                       String group, 
                       Date startTime, 
                       Date endTime, 
                       int repeatCount, 
                       long repeatInterval)

舉例以下:

    建立一個當即執行且僅執行一次的 SimpleTrigger:

SimpleTrigger trigger=
 new SimpleTrigger("myTrigger", "myGroup", new Date(), null, 0, 0L);

   建立一個半分鐘後開始執行,且每隔一分鐘重複執行一次的 SimpleTrigger:

SimpleTrigger trigger=
 new SimpleTrigger("myTrigger", "myGroup", 
    new Date(System.currentTimeMillis()+30*1000), null, 0, 60*1000);

   建立一個 2011 年 6 月 1 日 8:30 開始執行,每隔一小時執行一次,一共執行一百次,一天以後截止的 SimpleTrigger:

Calendar calendar = Calendar.getInstance(); 
calendar.set(Calendar.YEAR, 2011); 
calendar.set(Calendar.MONTH, Calendar.JUNE); 
calendar.set(Calendar.DAY_OF_MONTH, 1); 
calendar.set(Calendar.HOUR, 8); 
calendar.set(Calendar.MINUTE, 30); 
calendar.set(Calendar.SECOND, 0); 
calendar.set(Calendar.MILLISECOND, 0); 
Date startTime = calendar.getTime(); 
Date endTime = new Date (calendar.getTimeInMillis() +24*60*60*1000); 
SimpleTrigger trigger=new SimpleTrigger("myTrigger", 
       "myGroup", startTime, endTime, 100, 60*60*1000);

    上述最後一個例子中,同時設置了 end-time 與 repeat count,則優先考慮 end-time,總共能夠執行二十四次。

    CronTrigger 的用途更廣,相比基於特定時間間隔進行調度安排的 SimpleTrigger,CronTrigger 主要適用於基於日曆的調度安排。例如:每星期二的 16:38:10 執行,每個月一號執行,以及更復雜的調度安排等。

CronTrigger 一樣須要指定 start-time 和 end-time,其核心在於 Cron 表達式,由七個字段組成:  

Seconds 
 Minutes 
 Hours 
 Day-of-Month 
 Month 
 Day-of-Week 
 Year (Optional field)

 舉例以下:

   建立一個每三小時執行的 CronTrigger,且從每小時的整點開始執行:

0 0 0/3  * * ?

   建立一個每十分鐘執行的 CronTrigger,且從每小時的第三分鐘開始執行:

0 3/10 * * * ?

   建立一個每週一,週二,週三,週六的晚上 20:00 到 23:00,每半小時執行一次的 CronTrigger:

 0 0/30 20-23 ? * MON-WED,SAT

   建立一個每個月最後一個週四,中午 11:30-14:30,每小時執行一次的 trigger:

 0 30 11-14/1 ? * 5L


解釋一下上述例子中各符號的含義:

    首先全部字段都有本身特定的取值,例如,Seconds 和 Minutes 取值爲 0 到 59,Hours 取值爲 0 到 23,Day-of-Month 取值爲 0-31, Month 取值爲 0-11,或者 JAN,FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC,Days-of-Week 取值爲 1-7 或者 SUN, MON, TUE, WED, THU, FRI, SAT。每一個字段能夠取單個值,多個值,或一個範圍,例如 Day-of-Week 可取值爲「MON,TUE,SAT」,「MON-FRI」或者「TUE-THU,SUN」。

    通配符 * 表示該字段可接受任何可能取值。例如 Month 字段賦值 * 表示每月,Day-of-Week 字段賦值 * 表示一週的天天。

/ 表示開始時刻與間隔時段。例如 Minutes 字段賦值 2/10 表示在一個小時內每 20 分鐘執行一次,從第 2 分鐘開始。

? 僅適用於 Day-of-Month 和 Day-of-Week。? 表示對該字段不指定特定值。適用於須要對這兩個字段中的其中一個指定值,而對另外一個不指定值的狀況。通常狀況下,這兩個字段只需對一個賦值。

L 僅適用於 Day-of-Month 和 Day-of-Week。L 用於 Day-of-Month 表示該月最後一天。L 單獨用於 Day-of-Week 表示週六,不然表示一個月最後一個星期幾,例如 5L 或者 THUL 表示該月最後一個星期四。

W 僅適用於 Day-of-Month,表示離指定日期最近的一個工做日,例如 Day-of-Month 賦值爲 10W 表示該月離 10 號最近的一個工做日。

# 僅適用於 Day-of-Week,表示該月第 XXX 個星期幾。例如 Day-of-Week 賦值爲 5#2 或者 THU#2,表示該月第二個星期四。

   

  CronTrigger 的使用以下:

 CronTrigger cronTrigger = new CronTrigger("myTrigger", "myGroup"); 
 try { 
     cronTrigger.setCronExpression("0 0/30 20-13 ? * MON-WED,SAT"); 
 } catch (Exception e) { 
     e.printStackTrace(); 
 }

    Job 與 Trigger 的鬆耦合設計是 Quartz 的一大特色,其優勢在於同一個 Job 能夠綁定多個不一樣的 Trigger,同一個 Trigger 也能夠調度多個 Job,靈活性很強。

  

Listener

        除了上述基本的調度功能,Quartz 還提供了 listener 的功能。主要包含三種 listener:JobListener,TriggerListener 以及 SchedulerListener。當系統發生故障,相關人員須要被通知時,Listener 便能發揮它的做用。最多見的狀況是,當任務被執行時,系統發生故障,Listener 監聽到錯誤,當即發送郵件給管理員。下面給出 JobListener 的實例:

     清單 2. JobListener 的實現 

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.SchedulerException;


public class MyListener implements JobListener{

	@Override
	public String getName() {
		return "My Listener";
	}
	@Override
	public void jobWasExecuted(JobExecutionContext context,
			JobExecutionException jobException) {
		if(jobException != null){
			try {
				//中止Scheduler
				context.getScheduler().shutdown();
				System.out.println("
                Error occurs when executing jobs, shut down the scheduler ");
                // 給管理員發送郵件…
			} catch (SchedulerException e) {
				e.printStackTrace();
			}
		}
	}
}

        從清單 2 能夠看出,使用者只須要建立一個 JobListener 的繼承類,重載須要觸發的方法便可。固然,須要將 listener 的實現類註冊到 Scheduler 和 JobDetail 中:

sched.addJobListener(new MyListener()); 
 jobDetail.addJobListener("My Listener"); // listener 的名字

     

    使用者也能夠將 listener 註冊爲全局 listener,這樣即可以監聽 scheduler 中註冊的全部任務 :

sched.addGlobalJobListener(new MyListener());

    爲了測試 listener 的功能,能夠在 job 的 execute 方法中強制拋出異常。清單 7 中,listener 接收到異常,將 job 所在的 scheduler 停掉,阻止後續的 job 繼續執行。scheduler、jobDetail 等信息均可以從 listener 的參數 context 中檢索到。

    清單 2的輸出結果爲:

Generating report - myJob.myJob, type =FULL 
 Tue Feb 15 18:57:35 CST 2011 
 2011-2-15 18:57:35 org.quartz.core.JobRunShell run 
信息 : Job myJob.myJob threw a JobExecutionException: 
 org.quartz.JobExecutionException 
 at com.ibm.scheduler.QuartzListenerTest.execute(QuartzListenerTest.java:22) 
 at org.quartz.core.JobRunShell.run(JobRunShell.java:191) 
 at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:516) 
 2011-2-15 18:57:35 org.quartz.core.QuartzScheduler shutdown 
信息 : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down. 
 Error occurs when executing jobs, shut down the scheduler

TriggerListener、SchedulerListener 與 JobListener 有相似的功能,只是各自觸發的事件不一樣,如 JobListener 觸發的事件爲:

Job to be executed, Job has completed execution 等

TriggerListener 觸發的事件爲:

Trigger firings, trigger mis-firings, trigger completions 等

SchedulerListener 觸發的事件爲:

add a job/trigger, remove a job/trigger, shutdown a scheduler 等

讀者能夠根據本身的需求重載相應的事件。

相關文章
相關標籤/搜索