javascript
Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer
- 概述
- Timer 和 TimerTask
- 抽象類TimerTask
- Timer
- Timer構造函數及方法
- 示例
- Spring對Java Timer的支持 Spring40已經不支持了推薦使用Quartz
- ScheduledTimerTask
- MethodInvokingTimerTaskFactoryBean
- TimerFactoryBean
- 示例
- 示例源碼
概述
在Jdk1.3之后的版本中,通過java.util.Timer和java.util.TimerTask這兩個類提供了簡單的任務調度功能,稱之為Java Timer.
JavaTimer允許按照固定的頻率重復執行某項任務,比直接通過線程程序進行任務調度要輕松些,但是對于諸如“每周周一9:00執行”或者和日歷相關的任務調度就無能為力了。
此外,JDK Timer只適合對執行時間非常短的任務進行調度,因為在Timer中所有的TimerTask都在同一個背景線程中執行,長時間的任務會影響Timer的調度工作。
如果業務超過Java Timer的能力范圍,建議使用Quartz框架。
Timer 和 TimerTask
TimerTask表示一個需要多次執行的任務,它實現了Runnable接口,可以在run方法中定義業務邏輯
Timer負責指定調度規則并調度TimerTask
抽象類TimerTask
TimerTask相當于Quartz中的Job, 代表一個被調度的任務。
二者的區別在于,每當執行任務時,Quartz都會創建一個JOb實例,而Jdk Timer則使用相同的TimerTask實例。 所以如果TimerTask類中擁有狀態,那么這些狀態對于后面的執行是可見的。 從這一點上來講,TimerTask更像StatefulJob而非Job。
TimerTask抽象類中只有三個方法
public abstract void run() 子類覆蓋這個方法并定義任務的執行邏輯,每次執行任務時,run方法都會被調用一次
public boolean cancel()取消任務,假設任務被安排執行N次,那么在調用該方法后,后續的執行安排將取消
public long scheduledExecutionTime()返回此任務的計劃執行時間點。該方法一般在run方法內部調用,用戶可以通過該方法判斷本次執行的時間點是否過晚,并據此決定是否需要取消本次執行。該方法一般在固定執行頻率執行時才有意義。
Timer
Timer只能以如下方式對任務進行調度:在延遲一段時間或者在指定時間點后運行一次任務或周期性的運行任務.
實際上,在Timer內部使用Object#wait(long time)進行任務的時間調度。這種機制不能保證任務的實時執行,只是一個粗略的近似值。
每個Timer對象那個都有一個對應的“背景線程”,它負責調度并執行Timer中所有的TimerTask。 由于所有的TimerTask都在這線程中執行,所以TimerTask的執行時間應該比較短。 如果一個TimerTask的執行占用了過多的時間,后面的任務就會受到影響。 由于后續任務在調度時間上受到了“擠壓”,所以可能會造成扎堆執行的情況。
當Timer中所有的TimerTask已經執行完成并且Timer對象沒有外部引用時,Timer的任務執行線程才回結束,但這可能需要很長的時間。 因此Timer在默認情況下適用daemon Thread,這樣用戶就可以在應用程序中通過Time#cancel方法手工結束Timer.
如果希望盡快結束Timer中的任務,則可以調用TimerTask#cancel方法手工達到目的。
Java有兩種線程:用戶線程(User Thread)和守護線程(Daemon Thread).
守護線程是在在程序后臺運行,提供了一種通用服務線程,垃圾回收線程就是一種典型的守護線程。
守護線程是為主線程服務的,因此當所有的用戶線程都結束時,守護線程會自動終止,因此,也可以將守護線程形象的稱為奴仆線程,“主存我存,主亡我死”
將線程轉換為守護線程可以通過Thread對象的setDaemon(true)方法來實現
Timer構造函數及方法
Timer的構造函數在創建Timer對象的同時將啟動一個Timer背景線程。
我們先來看下Timer的構造函數
public Timer():創建一個Timer,背景線程是非守護線程
public Timer(String name):與Timer類似,只是通過name為關麗娜背景線程指定名稱
public Timer(boolean isDaemon) :創建一個Timer,當isDaemon為true時,背景線程為守護線程,守護線程將在應用程序主線程停止后自動退出。該方法為Java5.0新增的。
public Timer(String name, boolean isDaemon):與Timer(boolean isDaemon) 相似并為關聯背景線程指定名稱,該方法是5.0新增的
通過以下方法執行一次任務
public void schedule(TimerTask task, Date time) 在特定時間點執行一次任務
public void schedule(TimerTask task, long delay) 延遲指定時間后執行一次任務,delay的單位為毫秒
通過如下方法按照固定間隔執行任務,間隔時長為上次任務執行完成的時間點到下次任務開始執行的時間點,任務的執行可能產生時間的漂移
public void schedule(TimerTask task, Date firstTime, long period)從指定時間點開始周期性的執行任務,period的單位為毫秒,后一次執行將在前一次執行完成后開始計時。 比如任務安排每2S執行一次,假設第一次任務在0秒時間點開始執行并花費了1.5S , 這第二次將在第3.5秒時執行
public void schedule(TimerTask task, long delay, long period) 在延遲執行的時間后,周期性的執行任務
通過以下方法按照固定的頻率執行任務
public void scheduleAtFixedRate(TimerTask task, Date firstTime,
long period) 在指定時間點后,以指定頻率執行任務。 假設一個任務被安排每2S執行一次,如果第一次執行花費了1.5S , 則在0.5S后,第二次任務開始執行,以保證固定的執行頻率public void scheduleAtFixedRate(TimerTask task, long delay, long period) 在延遲一段時間后,以指定頻率執行任務。
此外Timer還有幾個控制方法
cancel :取消Timer的執行,并丟棄所有被調度的TimerTask不過正在執行的不受影響。 Timer被取消后,不能調度新的TimerTask
purge:將所有已經取消的TimerTask從Timer隊列中清除。 如果TimerTask沒有外部引用,那就可以被垃圾回收。 一般情況下無須調用該方法,只有在某些特定情況下,當一次性取消多個TimerTask后,調用該方法才有意義
示例
Job
package com.xgj.quartz.jdkTimer.jdkTimer;import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask;/*** * * @ClassName: MyTask* * @Description: 任務執行10次后退出* * @author: Mr.Yang* * @date: 2017年11月17日 下午5:01:32*/ public class MyTask extends TimerTask {int count = 0;@Overridepublic void run() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("Task begins to execute,execute times:" + count);Date date = new Date(scheduledExecutionTime());System.out.println("本次任務執行時間點為:" + sdf.format(date));// 執行10次后退出if (++count > 10) {cancel();System.out.println("Task exits");}}}調度類
package com.xgj.quartz.jdkTimer.jdkTimer;import java.util.Timer;public class TimerRunner {public static void main(String[] args) {Timer timer = new Timer();MyTask myTask = new MyTask();// 延遲1秒,每5S執行一次任務timer.schedule(myTask, 1000L, 5000L);} }運行結果
Task begins to execute,execute times:0 本次任務執行時間點為:2017-11-18 12:35:55 Task begins to execute,execute times:1 本次任務執行時間點為:2017-11-18 12:36:00 Task begins to execute,execute times:2 本次任務執行時間點為:2017-11-18 12:36:05 Task begins to execute,execute times:3 本次任務執行時間點為:2017-11-18 12:36:10 Task begins to execute,execute times:4 本次任務執行時間點為:2017-11-18 12:36:15 Task begins to execute,execute times:5 本次任務執行時間點為:2017-11-18 12:36:20 Task begins to execute,execute times:6 本次任務執行時間點為:2017-11-18 12:36:25 Task begins to execute,execute times:7 本次任務執行時間點為:2017-11-18 12:36:30 Task begins to execute,execute times:8 本次任務執行時間點為:2017-11-18 12:36:35 Task begins to execute,execute times:9 本次任務執行時間點為:2017-11-18 12:36:40 Task begins to execute,execute times:10 本次任務執行時間點為:2017-11-18 12:36:45 Task exitsSpring對Java Timer的支持 (Spring4.0+已經不支持了,推薦使用Quartz)
Spring在org.springframework.scheduling.timer中提供了幾個JDK Timer的支持類,主要在以下3個方面:
ScheduledTimerTask對TimerTask提供封裝并提供相關的配置
通過MethodInvokingTimerTaskFactoryBean類可以將一個Bean的方法封裝為TimerTask
通過TimerFactoryBean可以更方便的配置Timer。此外,讓Timer的生命周期和Spring容器的生命周期相關,在初始化TimerFactory后啟動Timer,在Spring容器關閉前取消Timer
ScheduledTimerTask
JDKTimer標準的API要求在使用Timer方法進行任務調度時才指定調度規則,不符合Bean的配置,Spring為此提供了ScheduledTimerTask,通過屬性指定任務和調度規則。
<bean id="scheduleTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"><property name="timerTask" ref="timeTask" /><!--指定調度任務 --><property name="delay" value="1000" /> <!--延遲時間,單位為毫秒 --><property name="period" value="1000" /> <!--周期時間,單位為毫秒 --></bean>如果希望只運行一次,則將period設置為0或者是負值。
默認情況下,采用固定時間間隔的調度方式,可以通過fixRate屬性設置以固定頻率的方式執行任務。
SimpleTimerTask還可以將實現了Runnable接口的類封裝成一個任務,用戶可以通過runnable屬性進行設置。
MethodInvokingTimerTaskFactoryBean
類似Quartz的MethodInvokingJobDetailFactoryBean,Spring也為 JDK Timer提供了一個方便類,用于將Bean封裝成一個TimerTask.
使用Spring 時,我們并不一定要繼承TimerTask 來定義一個任務,Spring 提供 org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean,如下所示
package com.xgj.quartz.jdkTimer.springJdkTimer2;import java.text.SimpleDateFormat; import java.util.Date;public class MyJobService {public void doJob() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("本次任務執行時間點為:" + sdf.format(new Date()));}}配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="myJobService" class="com.xgj.quartz.jdkTimer.springJdkTimer2.MyJobService" /><!-- Spring4.0以上版本已經不支持ScheduledTimerTask,而是推薦使用Quartz --><bean id="scheduleTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"><property name="targetObject" ref="myJobService" /><property name="targetMethod" value="doJob" /> </bean><bean id="scheduledTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"> <property name="timerTask" ref="scheduleTask"/> <property name="period" value="5000"/> <property name="delay" value="1000"/> </bean> <bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduledTimerTask" /></list></property></bean></beans>TimerFactoryBean
類似于Quartz的SchedulerFactoryBean,Spring為Timer提供了TimerFactoryBean類。 用戶可以將多個ScheduledTimerTask注冊到TimerFactoryBean中,TimerFactoryBean將返回一個Timer實例。 在初始化TimerFactory后啟動Timer,在Spring容器關閉前取消Timer
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduleTask" /></list></property></bean>scheduledTimerTasks的屬性類型為 ScheduledTimerTask[]可以注入多個ScheduledTimerTask .
此外TimerFactoryBean還有個daemon屬性,指定生成的Timer的背景線程是否是守護線程。
示例
為了演示該功能,我們需要先引入4.0以下的Spring依賴,這里我們使用
修改pom.xml中的如下信息
<spring.version>3.2.18.RELEASE</spring.version>官方說明:
https://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/scheduling/timer/ScheduledTimerTask.html
Job
package com.xgj.quartz.jdkTimer.springJdkTimer;import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask;/*** * * @ClassName: MyTask* * @Description: 任務執行10次后退出* * @author: Mr.Yang* * @date: 2017年11月17日 下午5:01:32*/ public class MyTask extends TimerTask {int count = 0;@Overridepublic void run() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("Task begins to execute,execute times:" + count);Date date = new Date(scheduledExecutionTime());System.out.println("本次任務執行時間點為:" + sdf.format(date));// 執行10次后退出if (++count > 10) {cancel();System.out.println("Task exits");}}}配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="timeTask" class="com.xgj.quartz.jdkTimer.springJdkTimer.MyTask" /><!-- Spring4.0以上版本已經不支持ScheduledTimerTask,而是推薦使用Quartz --><bean id="scheduleTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"><property name="timerTask" ref="timeTask" /><property name="delay" value="1000" /> <!--延遲1s --><property name="period" value="1000" /> <!--1s一次 --><property name="fixedRate" value="true" /></bean><bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduleTask" /></list></property></bean></beans>測試類(這種定時任務,寫單元測試只會執行一次,這里我們寫個簡單的main方法)
package com.xgj.quartz.jdkTimer.springJdkTimer;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringJdkTimerTest {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/quartz/jdkTimer/springJdkTimer/spring-jdkTimer.xml");System.out.println("initContext successfully");MyTask task = ctx.getBean("timeTask", MyTask.class);task.run();}}運行結果
2017-11-18 12:25:20,564 INFO [main] (AbstractApplicationContext.java:518) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@88b6cca: startup date [Sat Nov 18 12:25:20 BOT 2017]; root of context hierarchy 2017-11-18 12:25:20,658 INFO [main] (XmlBeanDefinitionReader.java:316) - Loading XML bean definitions from class path resource [com/xgj/quartz/jdkTimer/springJdkTimer/spring-jdkTimer.xml] 2017-11-18 12:25:21,084 INFO [main] (DefaultListableBeanFactory.java:605) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@381702ff: defining beans [timeTask,scheduleTask,timerFactory]; root of factory hierarchy 2017-11-18 12:25:21,230 INFO [main] (TimerFactoryBean.java:97) - Initializing Timer initContext successfully Task begins to execute,execute times:0 本次任務執行時間點為:2017-11-18 12:25:21 Task begins to execute,execute times:1 本次任務執行時間點為:2017-11-18 12:25:22 Task begins to execute,execute times:2 本次任務執行時間點為:2017-11-18 12:25:23 Task begins to execute,execute times:3 本次任務執行時間點為:2017-11-18 12:25:24 Task begins to execute,execute times:4 本次任務執行時間點為:2017-11-18 12:25:25 Task begins to execute,execute times:5 本次任務執行時間點為:2017-11-18 12:25:26 Task begins to execute,execute times:6 本次任務執行時間點為:2017-11-18 12:25:27 Task begins to execute,execute times:7 本次任務執行時間點為:2017-11-18 12:25:28 Task begins to execute,execute times:8 本次任務執行時間點為:2017-11-18 12:25:29 Task begins to execute,execute times:9 本次任務執行時間點為:2017-11-18 12:25:30 Task begins to execute,execute times:10 本次任務執行時間點為:2017-11-18 12:25:31 Task exits示例源碼
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
總結
以上是生活随笔為你收集整理的Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Quartz-SchedulerList
- 下一篇: Spring OXM- 漫谈XML解析技