いくつかのタスクスケジューリングのJava実装方法と比較2-読書

10981 ワード

Quartz
Quartzはより複雑なスケジューリング要件を満たすことができます.まず、毎週火曜日の16:38のスケジューリングをQuartzで実現する方法を見てみましょう.
インベントリ1:
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は両方を組み立て、タスクの実行を開始するようにトリガーします.
Job
利用者はJobの継承クラスを作成し,executeメソッドを実現するだけである.JobDetailは,JobおよびJobの属性をカプセル化し,これをSchedulerにパラメータとして提供する.Schedulerがタスクを実行するたびに、まずJobのインスタンスが作成され、executeメソッドの実行が呼び出されます.QuartzはJobに対してパラメータ付き構造関数を設計していないので,Jobの属性を追加のJobDataMapで格納する必要がある.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のデータは次のように取得できます.
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));

    }
}

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

Trigger
Triggerの役割はスケジューリングポリシーを設定することです.Quartzは、SimpleTriggerとCronTriggerが最も一般的なタイプのTriggerを設計しています.
SimpleTriggerは、ある特定の時間に1回実行するか、ある特定の時間間隔で複数回実行するのに適しています.上記の機能により、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);

30分後に実行を開始し、1分おきに繰り返し実行するSimpleTriggerを作成します.
SimpleTrigger trigger=
 new SimpleTrigger("myTrigger", "myGroup", 
    new Date(System.currentTimeMillis()+30*1000), null, 0, 60*1000);

2011年6月1日8時30分に実行を開始し、1時間ごとに実行し、合計100回実行し、1日後に終了する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を優先して合計24回実行できる.
CronTriggerは、特定の時間間隔でスケジュールされたSimpleTriggerよりも、カレンダーベースのスケジュールに主に適用されるため、広く使用されています.たとえば、毎週火曜日の16:38:10実行、毎月1日実行、さらに複雑なスケジュールなどです.
CronTriggerもstart-timeとend-timeを指定する必要があります.その核心はCron式で、7つのフィールドから構成されています.
Seconds 
 Minutes 
 Hours 
 Day-of-Month 
 Month 
 Day-of-Week 
 Year (Optional field)

例を次に示します.
3時間ごとに実行されるCronTriggerを作成し、1時間ごとの整数点から実行します.
0 0 0/3  * * ?

10分ごとに実行されるCronTriggerを作成し、3分ごとに実行を開始します.
0 3/10 * * * ?

毎週月曜日、火曜日、水曜日、土曜日の夜20:00から23:00まで、30分ごとにCronTriggerを実行します.
 0 0/30 20-23 ? * MON-WED,SAT

毎月最後の木曜日、昼11:30-14:30、1時間に1回の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フィールド付与*は1週間の毎日を表します.
開始時刻と間隔期間を示す.たとえば、Minutesフィールドの割り当て2/10は、2分目から1時間に20分ごとに実行されることを示します.
? Day-of-MonthとDay-of-Weekにのみ適用されます.?フィールドに特定の値が指定されていないことを示します.この2つのフィールドのいずれかに値を指定する必要があり、他のフィールドに値を指定しない場合に適用されます.一般的に、この2つのフィールドは1つの値を割り当てるだけです.
Lは、Day-of-MonthおよびDay-of-Weekにのみ適用されます.LはDay-of-Monthに使用され、この月の最後の日を表します.Lは単独でDay-of-Weekに土曜日を表し、そうでなければ1ヶ月の最後の曜日を表し、例えば5 LまたはTHULはその月の最後の木曜日を表す.
WはDay-of-Monthにのみ適用され、指定した日付に最も近い営業日を表します.たとえば、Day-of-Monthは10 Wの値を割り当て、その月が10日に最も近い営業日を表します.
#Day-of-Weekにのみ適用され、その月のXXX番目の曜日を示します.例えば、Day-of-Weekは5#2またはTHU#2に割り当てられ、その月の第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の機能も提供しています.主に3種類の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の継承クラスを1つ作成するだけで,トリガが必要なメソッドを再ロードすればよい.もちろん、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など
読者は自分のニーズに応じて対応するイベントを再ロードすることができます.