spring4定時器 cronTrigger和simpleTrigger實現方法
Quartz 是個開源的作業調度框架,為在 Java 應用程序中進行作業調度提供了簡單卻強大的機制。Quartz 允許開發人員根據時間間隔(或天)來調度作業。它實現了作業和觸發器的多對多關系,還能把多個作業與不同的觸發器關聯。整合了 Quartz 的應用程序可以重用來自不同事件的作業,還可以為一個事件組合多個作業。
SimpleTrigger 當需要在規定的時間執行一次或在規定的時間段以一定的時間間隔重復觸發執行Job時,SimpleTrigger就可以滿足要求;SimpleTrigger的屬性有:開始時間、結束時間、重復次數和重復的時間間隔,重復次數屬性的值可以為0、正整數、或常量 SimpleTrigger.REPEAT_INDEFINITELY,重復的時間間隔屬性值必須為0或長整型的正整數,以毫秒作為時間單位,當重復的時 間間隔為0時,意味着與Trigger同時觸發執行(或幾乎與Scheduler開始時同時觸發執行)。如果有指定結束時間屬性值,則結束時間屬性優先於重復次數屬性,這樣的好處在於:當我們需要創建一個每間隔10秒鍾觸發一次直到指定的結束時間的 Trigger,而無需去計算從開始到結束的所重復的次數,我們只需簡單的指定結束時間和使用REPEAT_INDEFINITELY作為重復次數的屬性 值即可(我們也可以指定一個比在指定結束時間到達時實際執行次數大的重復次數)。
CronTrigger 支持比 SimpleTrigger 更具體的調度,而且也不是很復雜。基於 cron 表達式,CronTrigger 支持類似日歷的重復間隔,而不是單一的時間間隔。
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 , - * /
Quartz官方網站對SimpleTrigger和CronTrigger的簡單對比:
SimpleTrigger is handy if you need 'one-shot' execution (just single execution of a job at a given moment in time), or if you need to fire a job at a given time, and have it repeat N times, with a delay of T between executions.當你需要的是一次性的調度(僅是安排單獨的任務在指定的時間及時執行),或者你需要在指定的時間激活某個任務並執行N次,設置每次任務執行的間隔時間T。那此時使用SimpleTrigger將是非常方便的。
CronTrigger is useful if you wish to have triggering based on calendar-like schedules - such as "every Friday, at noon" or "at 10:15 on the 10th day of every month."如果你需要安排的任務時基於日期的-比如"每個星期五正午"或者"每個月10號的10:15",使用CronTrigger將是非常有用的。
1、配置applicationcontext.xml
<!-- cronTrigger實現方式 --> <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.ouku.entities.report.ReportTimerTask" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="3600" /> </map> </property> </bean> <bean id="springUtil" class="com.ouku.util.SpringUtil" /> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="exampleJob" /> <!-- run every morning at 6 AM --> <!-- <property name="cronExpression" value="0 0 6 * * ?" /> --> <!-- <property name="cronExpression" value="0 0/1 * * * ?" /> --><!-- 每分鍾 --> <property name="cronExpression" value="0/5 * * * * ?" /> <!-- 每秒 --> </bean> <bean id="exampleBusinessObject" class="com.ouku.entities.report.ReportTimerTaskTwo" /> <bean id="jobDetail" <!-- simpleTrigger實現方式 --> class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> <property name="concurrent" value="false" /> </bean> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <!-- see the example of method invoking job above --> <property name="jobDetail" ref="jobDetail" /> <!-- 0 seconds --> <property name="startDelay" value="0" /> <!-- repeat every 5 seconds --> <property name="repeatInterval" value="5000" /> </bean> <!-- 總調度用於啟動Spring定時器 --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> <ref bean="simpleTrigger"/> </list> </property> </bean>
其中<property name="concurrent" value="false" />
- 設置為false時,定時任務會串行執行;就是定時任務開啟時,知道這個job結束,才會執行下一個定時任務;
- 設置為true時,定時任務會並發執行,就是不管這個job有沒有執行完,定時任務都會啟動,如果沒有執行完,定時任務會開一個新的線程來執行job,確保能在設定的時間間隔內執行job;定時器默認最多有十個線程,當十個線程都用完時,定時任務會阻塞,直到有新的線程可用,才會開啟定時任務去執行job;
2.利用cronTrigger的Java實現
package com.ouku.entities.report; import java.util.Date; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import com.ouku.report.ReportTask; public class ReportTimerTask extends QuartzJobBean { private int timeout; public void setTimeout(int timeout) { this.timeout = timeout; } @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { try { Date date = new Date(); String dd = " " + date.getMinutes() + ":" + date.getSeconds() + " "; System.out.println("AAA" + dd); //to do } catch (Exception e) { e.printStackTrace(); } } }
3.利用simpleTrigger的Java實現
package com.ouku.entities.report; import java.util.Date; import org.quartz.JobExecutionException; import com.ouku.report.ReportTask; public class ReportTimerTaskTwo { public void doIt() throws JobExecutionException { try { Date date = new Date(); String dd = " " + date.getMinutes() + ":" + date.getSeconds() + " "; System.out.println("sss1" + dd); //to do .. } catch (Exception e) { e.printStackTrace(); } } }
在使用並發的情況下,當Job執行時間超過間隔時間時,調度框架為了能讓任務按照我們預定的時間間隔執行,會馬上啟用新的線程執行任務。
再次強調,spring4 quartz最多可以為我們開啟十個線程,當我們需要執行的任務有鎖的情況下,那么在十個線程用完之后,定時器沒有線程可以開啟,這時候就會出現定時任務的時間間隔超過我們設定的時間間隔;
我們怎么解決這個情況呢?
一、優化我們執行的job,使其在設定的時間間隔內執行完;
二、設置時間間隔更長一點;
我們再來看看串行執行和並發執行的區別:
下面是串行執行時,每個線程的執行情況:
我們可以看到,每個定時任務是在job完成之后才會開啟新的線程來執行下一個job;
下面看看並發執行,每個線程執行的情況:
我們可以看到,定時任務是按照我們設置的時間間隔執行的,不會在意job是否執行完,如果沒有執行完,定時任務會開啟一個新的線程來執行job;