1. 程式人生 > 其它 >java的四種定時任務

java的四種定時任務

java的四種定時任務

Timer

/*
  使用 Timer 實現任務排程的核心類是 Timer 和 TimerTask。其中 Timer 負責設定 TimerTask 的起始與間隔執行時間。使用者只需要建立一個 TimerTask 的繼承類,實現自己的 run 方法,然後將其丟給 Timer 去執行即可。
  Timer 的設計核心是一個 TaskList 和一個 TaskThread。Timer 將接收到的任務丟到自己的 TaskList 中,TaskList 按照 Task 的最初執行時間進行排序。TimerThread 在建立 Timer 時會啟動成為一個守護執行緒。這個執行緒會輪詢所有任務,找到一個最近要執行的任務,然後休眠,當到達最近要執行任務的開始時間點,TimerThread 被喚醒並執行該任務。之後 TimerThread 更新最近一個要執行的任務,繼續休眠。
  Timer 的優點在於簡單易用,但由於所有任務都是由同一個執行緒來排程,因此所有任務都是序列執行的,同一時間只能有一個任務在執行,前一個任務的延遲或異常都將會影響到之後的任務。
*/
public class TimerTest extends TimerTask {

    private String jobName = "";

    public TimerTest(String jobName) {
        super();
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        long delay1 = 1 * 1000;
        long period1 = 1000;
        // 從現在開始 1 秒鐘之後,每隔 1 秒鐘執行一次 job1
        timer.schedule(new TimerTest("job1"), delay1, period1);
        long delay2 = 2 * 1000;
        long period2 = 2000;
        // 從現在開始 2 秒鐘之後,每隔 2 秒鐘執行一次 job2
        timer.schedule(new TimerTest("job2"), delay2, period2);
    }
}

ScheduledExecutor

/*
程式碼展示了 ScheduledExecutorService 中兩種最常用的排程方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。ScheduleAtFixedRate 每次執行時間為上一次任務開始起向後推一個時間間隔,即每次執行時間為 :initialDelay, initialDelay+period, initialDelay+2*period, …;ScheduleWithFixedDelay 每次執行時間為上一次任務結束起向後推一個時間間隔,即每次執行時間為:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。由此可見,ScheduleAtFixedRate 是基於固定時間間隔進行任務排程,ScheduleWithFixedDelay 取決於每次任務執行的時間長短,是基於不固定時間間隔進行任務排程。
Timer 和 ScheduledExecutor 都僅能提供基於開始時間與重複間隔的任務排程,不能勝任更加複雜的排程需求。比如,設定每星期二的 16:38:10 執行任務。該功能使用 Timer 和 ScheduledExecutor 都不能直接實現,但我們可以藉助 Calendar 間接實現該功能。
*/
public class ScheduledExecutorTest implements Runnable {
    private String jobName = "";

    public ScheduledExecutorTest(String jobName) {
        super();
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        long initialDelay1 = 1;
        long period1 = 1;
        // 從現在開始1秒鐘之後,每隔1秒鐘執行一次job1
        service.scheduleAtFixedRate(
                new ScheduledExecutorTest("job1"), initialDelay1,
                period1, TimeUnit.SECONDS);

        long initialDelay2 = 1;
        long delay2 = 1;
        // 從現在開始2秒鐘之後,每隔2秒鐘執行一次job2
        service.scheduleWithFixedDelay(
                new ScheduledExecutorTest("job2"), initialDelay2,
                delay2, TimeUnit.SECONDS);
    }
}

Quartz

/*
Quartz 設計的核心類包括 Scheduler, Job 以及 Trigger。其中,Job 負責定義需要執行的任務,Trigger 負責設定排程策略,Scheduler 將二者組裝在一起,並觸發任務開始執行。
使用者只需要建立一個 Job 的繼承類,實現 execute 方法。JobDetail 負責封裝 Job 以及 Job 的屬性,並將其提供給 Scheduler 作為引數。每次 Scheduler 執行任務時,首先會建立一個 Job 的例項,然後再呼叫 execute 方法執行。Quartz 沒有為 Job 設計帶引數的建構函式,因此需要通過額外的 JobDataMap 來儲存 Job 的屬性。JobDataMap 可以儲存任意數量的 Key,Value 對。
*/

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();
        }
    }
} 

JCronTab

/*
JCronTab 與 Quartz 相比,其優點在於,第一,支援多種任務排程的持久化方法,包括普通檔案、資料庫以及 XML 檔案進行持久化;第二,JCronTab 能夠非常方便地與 Web 應用伺服器相結合,任務排程可以隨 Web 應用伺服器的啟動自動啟動;第三,JCronTab 還內建了發郵件功能,可以將任務執行結果方便地傳送給需要被通知的人。
*/

結束語

對於簡單的基於起始時間點與時間間隔的任務排程,使用 Timer 就足夠了;如果需要同時排程多個任務,基於執行緒池的 ScheduledTimer 是更為合適的選擇;當任務排程的策略複雜到難以憑藉起始時間點與時間間隔來描述時,Quartz 與 JCronTab 則體現出它們的優勢。熟悉 Unix/Linux 的開發人員更傾向於 JCronTab,且 JCronTab 更適合與 Web 應用伺服器相結合。Quartz 的 Trigger 與 Job 鬆耦合設計使其更適用於 Job 與 Trigger 的多對多應用場景。