java定時器、Spring定時器和Quartz定時器

當前java程序中可以實現定時的主要有三種方式,分別是:java定時器,spring定時器,quartz定時器。java

   下面依次講講他們的應用!spring

java定時器的應用
   其實java很早就有解決定時器任務的方法了,java提供了類java.util.TimerTask類基於線程的方式來實現定時任務的操做,而後再提供java.util.Timer類來註冊調用,先建立一個類 RingTask 繼承 java.util.TimerTask,實現run方法,相關代碼以下:服務器

package timer;
 
import java.util.TimerTask;
 
/**
  * 這是一個打鈴的程序,必須隔一段時間打一次
  */
public class RingTask extends TimerTask{
 
    public RingTask() {
        // TODO Auto-generated constructor stub
    }
 
    public RingTask(int s,int d) {
        // TODO Auto-generated constructor stub
        this.second = s;
        this.delay  = d;
    }
 
    int second = 1;
 
    int delay  = 1;
 
    public void setSecond(int second) {
        this.second = second;
    }
 
    public void setDelay(int delay) {
        this.delay = delay;
    }
 
    @Override
 
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("我是打鈴程序!"+"我第一次打鈴延遲了"+delay+"秒!");
        System.out.println("打鈴了!每過"+second+"秒一次");
    }
 
}
 
//定義好後,下面須要註冊調用了,註冊調用的方法以下:
public static void main(String[] args) {
    //以 java定時器的模式調用
    Timer timer = new Timer();
    timer.schedule(
            new RingTask(3,3),  //須要註冊的定時類
            3000,             //最開始先延遲3秒的時間
            3000);            //每隔3秒的時間調用一次
}


   一個簡單的java定時器就寫好了,方便而簡介,可是有很差的缺點: 若是須要實現天天早晨7點鐘的定時執行一次,且週末的時候早晨7點鐘不須要提醒,那這個可就不夠用了,而且若是須要服務器一開啓就觸發這個定時器,則這種註冊調用的方法也是不行的。併發

Spring定時器的應用
   spring定時器是在spring框架中應用較成熟的一種方式,spring將定時任務的調用部分提到了配置文件當中,使定時器的觸發條件變得更加靈活,spring定時器的實現,仍然須要 繼承 java.util.TimerTask,實現run方法 ,示例類上面已給出,調用的配置以下: 框架

<!-- 定時器的配置 (spring定時器)-->
<!-- 要調度的bean配置 -->
<bean id="ringTask" class="timer.RingTask">
    <!-- 給 屬性 second 賦值 爲 3 -->    
    <property name="second" >    
        <value>3</value>        
    </property>
    <!-- 給 屬性 delay 賦值 爲 3 -->
    <property name="delay" >
        <value>3</value>    
    </property>
</bean>
<!--配置一個觸發器 配置觸發器的參數-->
<bean id="scheduleRingTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
    <property name="delay" value="3000"></property>           <!--第一次延遲3秒的時間-->
    <property name="period" value="3000"></property>          <!--每隔3秒的時間執行一次-->
    <property name="timerTask" ref="ringTask"></property>   <!--制定觸發的類-->
</bean>
<!-- 總調度,用於啓動定時器 -->
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
    <property name="scheduledTimerTasks">
        <list>    
            <ref bean="scheduleRingTask"/>    
        </list>
    </property>
</bean>


   在調用方面是否是靈活些了,且可以實現服務器已啓動,就將定時器的執行歸入的被監控的範圍,符合條件立刻觸發執行。可是仍是存在缺點: 對於指定了具體的年月日時分秒而執行的任務仍是不能解決。ide

Quartz定時器
  Quartz是基於Spring框架之上的更增強大的定時器,它不只能夠輕鬆的實現前面兩種定時器的功能,還實現了很是繁複的時間觸發執行的任務,Quartz有兩種方式來調度定時任務,一是使用Spring提供的 MethodInvokingJobDetailFactoryBean 代理類,Quartz經過該代理類直接調度任務類的某個函數;二是任務類繼承QuartzJobBean類或者實現org.quartz.Job接口,Quartz經過該父類或者接口進行調度。函數

   先來看看實現前面個兩種定時器的功能,如今先來舉個例子,好比燒水,每1小時燒開一次,進行定時提醒,而後從新換水,再燒。this

   具體的燒水定時類代碼以下:線程

package timer;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
/**
 * 這是一個熱水的程序,必要要通過一段時間燒熱了,才提醒從新換水
 * 這個類無論是繼承 QuartzJobBean仍是實現org.quartz.Job都行
 */
public class HotWaterTask extends QuartzJobBean /*implements Job*/{
    // public void execute(JobExecutionContext arg0) 
    // throws JobExecutionException {
        // // TODO Auto-generated method stub
        // System.out.println("我是熱水程序,我第一燒水須要1小時");
        // System.out.println("水如今燒開了,要及時換水哦!");
    // }
    
    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
        // TODO Auto-generated method stub
        System.out.println("我是熱水程序, 我第一燒水須要1小時 ");
        System.out.println("水如今燒開了,要及時換水哦!");
    }
}
類定義好了,下面須要配置進去,配置的代碼以下:

<!-- 配置須要調度的任務類 -->
<bean id="hotWaterTask" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="timer.HotWaterTask"></property>
</bean>
<!-- 配置一個觸發器 -->
<bean id="hotWaterTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail" ref="hotWaterTask"></property>
    <property name="startDelay" value="3600000"></property>
    <property name="repeatInterval" value=" 3600000"></property>
</bean>
<!-- 總調度,用於啓動定時器 -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers" >
        <list>
            <ref bean="hotWaterTrigger"/>
        </list>
    </property>
</bean>
  看到了嗎,在這裏咱們並無直接聲明一個 HotWaterTask  Bean,而是聲明瞭一個JobDetailBean。這個是Quartz的特色。JobDetailBean是Quartz的org.quartz.JobDetail的子類,它要求經過jobClass屬性來設置一個Job對象。

  好了,上面的任務已經實現了,下面看看 如何實現 具體的年月日時分秒執行的代碼

Quartz在指定的時間執行 (很強大的代理定時執行機制)

  (1) 定義上班鬧鐘定時類代碼以下: 

package timer;
 
/**
 * 開始上班,這個程序要求天天(非週末)早晨八點須要啓動一次
 */
public class StartWorkJob {
    public void startWork(){
        System.out.println("我是上班程序,天天(非週末)早晨八點須要啓動一次");
        System.out.println("上班了!~")
    }
}
  看到了嗎,這個類StartWorkJob 並無繼承任何類也沒有實現任何接口,且方法 startWork也是本身定義的,原有的業務代碼不須要作任何更改。下面就要提到Quartz實現的一種機制,經過Spring提供的代理類(MethodInvokingJobDetailFactoryBean)來實現定時任務,這個類只須要提供它要代理的類以及要代理的方法,就可以很好的就行定時監控了,強大吧,相關的代碼以下:

<!-- 配置須要定時的bean類 -->
<bean id="startWorkJob" class="timer.StartWorkJob"></bean>
<!-- 配置任務的具體類和方法 -->
<bean id="startWorkTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <!-- 要調用的bean -->
    <property name="targetObject" ref="startWorkJob"></property> 
    <!-- 要調用的Method -->
    <property name="targetMethod" value="startWork"></property>
    <!-- 是否併發,false表示 若是發生錯誤也不影響下一次的調用 -->
    <property name="concurrent" value="false"></property>
</bean>
<!-- 配置一個觸發器 -->
<bean id="startWorkTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="startWorkTask"></property>
    <property name="cronExpression" value="0 * 13 * * ?"></property> <!--天天的下午1點的每分鐘的0秒都執行一次-->
</bean>
 
<!-- 總調度,用於啓動定時器 -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers" >
        <list>
            <ref bean="startWorkTrigger"/>
        </list>
    </property>
</bean>


   好了一個指定了具體時間的定時觸發任務也已經實現了,下面來看看cronExpression 有哪些須要知道的配置信息,信息以下:代理

   一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。從左到右:

1.秒2.分3.小時4.月份中的日期(1-31)5.月份(1-12或JAN-DEC)6.星期中的日期(1-7或SUN-SAT)7.年份(1970-2099) 
每一個元素都顯示的規定一個值(如6),一個區間(9-12),一個列表(9,11,13)或一個通配符(*)。由於4和6這兩個元素是互斥的,所以應該經過設置一個問號(?)來代表不想設置的那個字段,「/」若是值組合就表示重複次數(10/6表示每10秒重複6次)。

最後提供一個在線cron表達式生成器:http://cron.qqe2.com/