Java執行定時任務(Timer、Quartz)

最近用到定時任務,這裏總結一下java中常用的幾種定時方法。
Java自帶的java.util.Timer類,這個類允許你調度一個java.util.TimerTask任務。使用這種方式可以讓你的程序按照某一個頻度執行,但不能在指定時間運行。一般用的較少。 Spring和QuartZ都支持cron,功能都很強大,Spring的優點是稍微簡單一點,QuartZ的優點是沒有Spring也可使用;

一、Timer

這裏我們看一下例子:

package test;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 一個實現TimerTask的類(執行具體任務)
 * @author cye
 *
 */
public class TimerTest extends TimerTask {
    private Timer timer;

    public static void main(String[] args) {
        TimerTest timerTest = new TimerTest();
        timerTest.timer = new Timer();
        // 立刻開始執行timerTest任務,只執行一次
        // timerTest.timer.schedule(timerTest, new Date());
        // 立刻開始執行timerTest任務,執行完本次任務後,隔2秒再執行一次
        // timerTest.timer.schedule(timerTest,new Date(),2000);
        // 一秒鐘後開始執行timerTest任務,只執行一次
        // timerTest.timer.schedule(timerTest,1000);
        // 一秒鐘後開始執行timerTest任務,執行完本次任務後,隔2秒再執行一次
        // timerTest.timer.schedule(timerTest,1000,2000);
        // 立刻開始執行timerTest任務,每隔2秒執行一次
        timerTest.timer.scheduleAtFixedRate(timerTest, new Date(), 2000);
        // 一秒鐘後開始執行timerTest任務,每隔2秒執行一次
        // timerTest.timer.scheduleAtFixedRate(timerTest,1000,2000);

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 結束任務執行,程序終止
        timerTest.timer.cancel();
        // 結束任務執行,程序並不終止,因爲線程是JVM級別的
        // timerTest.cancel();
    }

    @Override
    public void run() {
        System.out.println("Task is running!");
    }
}

二、Quartz

Quartz 是個開源的作業調度框架,爲在 Java 應用程序中進行作業調度提供了簡單卻強大的機制。Quartz 允許開發人員根據時間間隔(或天)來調度作業。它實現了作業和觸發器的多對多關係,還能把多個作業與不同的觸發器關聯。整合了 Quartz 的應用程序可以重用來自不同事件的作業,還可以爲一個事件組合多個作業。雖然可以通過屬性文件(在屬性文件中可以指定 JDBC 事務的數據源、全局作業和/或觸發器偵聽器、插件、線程池,以及更多)配置 Quartz,但它根本沒有與應用程序服務器的上下文或引用集成在一起。結果就是作業不能訪問 Web 服務器的內部函數;例如,在使用 WebSphere 應用服務器時,由 Quartz 調度的作業並不能影響服務器的動態緩存和數據源。

作業和觸發器

Quartz 調度包的兩個基本單元是作業和觸發器。
調度器:調度器用於將與作業觸發器關聯,一個作業可關聯多個觸發器,這樣每個觸發器被可以觸發的作業執行;一個觸發器可用於控制多個作業,觸發觸發時,全部作業將獲得調度。Quartz的調度器由Scheduler接口體現。
作業 是能夠調度的可執行任務,觸發器 提供了對作業的調度(有SimpleTrigger和CronTrigger兩種類型)。雖然這兩個實體很容易合在一起,但在 Quartz 中將它們分離開來是有原因的,而且也很有益處。通過把要執行的工作與它的調度分開,Quartz 允許在不丟失作業本身或作業的上下文的情況下,修改調度觸發器。而且,任何單個的作業都可以有多個觸發器與其關聯。

  1. 作業: 首先創建一個實現了org.quartz.Job接口的類,並實現這個接口的唯一一個方法execute(JobExecutionContext arg0) throws JobExecutionException
package com.crystal.springmvc.timer;

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

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

/**
 * @author cye
 */
public class SimpleQuartzJob implements Job {

    /**
     * @param context:作業實例的運行時上下文,它提供了對調度器和觸發器的訪問,這兩者協作來啓動作業以及作業的 JobDetail 對象的執行。
     *  Quartz 通過把作業的狀態放在 JobDetail 對象中並讓 JobDetail 構造函數啓動一個作業的實例,分離了作業的執行和作業周圍的狀態。
     *  JobDetail 對象儲存作業的偵聽器、羣組、數據映射、描述以及作業的其他屬性。
     */
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        System.out.println("In SimpleQuartzJob - executing its JOB at " + sdf.format(new Date()) + " by " + context.getTrigger());
    }
}

2.Cron 觸發器
CronTrigger 基於 cron 表達式,支持類似日曆的重複間隔

package com.crystal.springmvc.timer;

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

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleTriggerRunner {
    public void task() throws SchedulerException {
        //實例化一個 SchedulerFactory,獲得此調度器
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();


        // 初始化job任務
        JobDetail jobDetail = JobBuilder.newJob(SimpleQuartzJob.class)
                .withIdentity("jobDetail-s1", "jobDetailGroup-s1").build();
        // 初始化觸發器 CronTrigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup-s1")
                .startAt(new Date()).withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();
        Date ft = scheduler.scheduleJob(jobDetail, trigger); // 註冊並進行調度
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        System.out.println(jobDetail.getKey() + " 已被安排執行於: " + sdf.format(ft)
                + ",並且以如下重複規則重複執行: " + trigger.getCronExpression());

        scheduler.start();  // 開始執行,start()方法被調用後,計時器就開始工作,計時調度中允許放入N個Job
        try { // 主線程等待一分鐘
            Thread.sleep(60L * 1000L);
        } catch (Exception e) {
        }
        scheduler.shutdown(true);   // 關閉定時調度,定時器不再工作
    }
}

Cron 表達式包括以下 7 個字段:
格式: [秒] [分] [小時] [日] [月] [周] [年]

序號 說明 是否必填 允許填寫的值 允許的通配符
1 0-59 , - * /
2 0-59 , - * /
3 小時 0-23 , - * /
4 1-31 , - * ? / L W
5 1-12 or JAN-DEC , - * /
6 1-7 or SUN-SAT , - * ? / L #
7 empty 或 1970-2099 , - * /

通配符說明:
* 表示所有值. 例如:在分的字段上設置 “*”,表示每一分鐘都會觸發。
? 表示不指定值。使用的場景爲不需要關心當前設置這個字段的值。例如:要在每月的10號觸發一個操作,但不關心是周幾,所以需要周位置的那個字段設置爲”?” 具體設置爲 0 0 0 10 * ?
- 表示區間。例如 在小時上設置 “10-12”,表示 10,11,12點都會觸發。
* , 表示指定多個值,例如在周字段上設置 “MON,WED,FRI” 表示週一,週三和週五觸發
/ 用於遞增觸發。如在秒上面設置”5/15” 表示從5秒開始,每增15秒觸發(5,20,35,50)。在月字段上設置’1/3’所示每月1號開始,每隔三天觸發一次。
L 表示最後的意思。在日字段設置上,表示當月的最後一天(依據當前月份,如果是二月還會依據是否是潤年[leap]), 在周字段上表示星期六,相當於”7”或”SAT”。如果在”L”前加上數字,則表示該數據的最後一個。例如在周字段上設置”6L”這樣的格式,則表示“本月最後一個星期五”
W 表示離指定日期的最近那個工作日(週一至週五). 例如在日字段上設置”15W”,表示離每月15號最近的那個工作日觸發。如果15號正好是週六,則找最近的週五(14號)觸發, 如果15號是周未,則找最近的下週一(16號)觸發.如果15號正好在工作日(週一至週五),則就在該天觸發。如果指定格式爲 “1W”,它則表示每月1號往後最近的工作日觸發。如果1號正是週六,則將在3號下週一觸發。(注,”W”前只能設置具體的數字,不允許區間”-“).
注意: ‘L’和 ‘W’可以一組合使用。如果在日字段上設置”LW”,則表示在本月的最後一個工作日觸發(一般指發工資 )
# 序號(表示每月的第幾個周幾),例如在周字段上設置”6#3”表示在每月的第三個週六.注意如果指定”#5”,正好第五週沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了)
注意:周字段的設置,若使用英文字母是不區分大小寫的 MON 與mon相同.

Corn表達式在線驗證:http://cron.qqe2.com/

發佈了60 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章