上次熟悉了MarkDown的用法之后,由于各種原因一直沒(méi)有時(shí)間更新博客。。。這次打算把我之前總結(jié)的一些東西陸陸續(xù)續(xù)的寫(xiě)在博客里外冀,希望下次用到的時(shí)候能夠快速記起來(lái)~
---------------------------------------華麗的分割線--------------------------------------------
1 定時(shí)任務(wù)簡(jiǎn)介
在應(yīng)用開(kāi)發(fā)中,經(jīng)常需要一些周期性的操作掀泳,如:需要在每天凌晨時(shí)候分析一次前一天的日志信息雪隧、需要每隔5分鐘檢查一下某個(gè)模塊是否有異常然后自動(dòng)發(fā)送郵件給管理員,在項(xiàng)目運(yùn)行到第30天的時(shí)候需要執(zhí)行某些操作等等员舵。這些功能需求就需要我們使用一些定時(shí)任務(wù)方法去實(shí)現(xiàn)脑沿,本文將介紹目前J2EE項(xiàng)目常用的幾種定時(shí)任務(wù)方法并比較它們的優(yōu)缺點(diǎn)。
2 J2EE項(xiàng)目中常用到的三種定時(shí)任務(wù)實(shí)現(xiàn)方法
2.1 java.util.Timer類
2.1.1 簡(jiǎn)介
先來(lái)介紹下Java自帶的java.util.Timer類马僻,這個(gè)類允許你調(diào)度一個(gè)java.util.TimerTask任務(wù)庄拇。使用這種方式可以讓你的程序按照某一個(gè)頻度執(zhí)行,但不能在指定時(shí)間運(yùn)行。TimerTask類用于實(shí)現(xiàn)由Timer安排的一次或重復(fù)執(zhí)行的某個(gè)任務(wù)措近。每一個(gè)Timer對(duì)象對(duì)應(yīng)的是一個(gè)線程溶弟,因此計(jì)時(shí)器所執(zhí)行的任務(wù)應(yīng)該迅速完成,否則會(huì)延遲后續(xù)的任務(wù)執(zhí)行瞭郑。
java.util.Timer類方法摘要
void cancel()
終止此計(jì)時(shí)器辜御,丟棄所有當(dāng)前已安排的任務(wù)。
int purge()
從此計(jì)時(shí)器的任務(wù)隊(duì)列中移除所有已取消的任務(wù)屈张。
void schedule(TimerTask task, Date time)
安排在指定的時(shí)間執(zhí)行指定的任務(wù)擒权。
void schedule(TimerTask task, Date firstTime, long period)
安排指定的任務(wù)在指定的時(shí)間開(kāi)始進(jìn)行重復(fù)的固定延遲執(zhí)行。
void schedule(TimerTask task, long delay)
安排在指定延遲后執(zhí)行指定的任務(wù)阁谆。
void schedule(TimerTask task, long delay, long period)
安排指定的任務(wù)從指定的延遲后開(kāi)始進(jìn)行重復(fù)的固定延遲執(zhí)行碳抄。
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
安排指定的任務(wù)在指定的時(shí)間開(kāi)始進(jìn)行重復(fù)的固定速率執(zhí)行。
void scheduleAtFixedRate(TimerTask task, long delay, long period)
安排指定的任務(wù)在指定的延遲后開(kāi)始進(jìn)行重復(fù)的固定速率執(zhí)行笛厦。
TimerTask類方法摘要
boolean cancel()
取消此計(jì)時(shí)器任務(wù)纳鼎。
abstract void run()
此計(jì)時(shí)器任務(wù)要執(zhí)行的操作。
long scheduledExecutionTime()
返回此任務(wù)最近實(shí)際執(zhí)行的安排執(zhí)行時(shí)間裳凸。
2.1.2 使用方法
使用Timer類的schedule(TimerTask task, long delay, long period)方法啟動(dòng)定時(shí)器。
Timer timer=new Timer();
MyTask myTask=new MyTask();
timer.schedule(myTask, 1000, 2000);
TimerTask類主要實(shí)現(xiàn)run()方法里的業(yè)務(wù)邏輯劝贸,用法如下:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
public class MyTask extends TimerTask {
@Override
public void run() {
// TODO Auto-generated method stub
SimpleDateFormat simpleDateFormat=null;
simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
System.out.println("當(dāng)前的系統(tǒng)時(shí)間為:"+simpleDateFormat.format(new Date()));
}
}
2.1.3 擴(kuò)展內(nèi)容(往定時(shí)任務(wù)方法中傳參數(shù))
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
public class WaitListTimerTask extends TimerTask {
private WaitList waitList;
public WaitListTimerTask(WaitList waitList){
this.waitList=waitList;
}
@Override
public void run() {
// 參數(shù)waitList使用示例
List<CourseWaitList> allCourseWaitList = this.waitList.getAllCourseWaitList();
}
}
2.2 Spring3.0以后自帶的Spring-task
2.2.1 簡(jiǎn)介
Spring3.0以后自主開(kāi)發(fā)了定時(shí)任務(wù)工具spring task姨谷,可以將它比作一個(gè)輕量級(jí)的Quartz,而且使用起來(lái)很簡(jiǎn)單映九,除spring相關(guān)的包外不需要額外的包梦湘,而且支持注解和配置文件兩種形式,下面將分別介紹這兩種方式件甥。
2.2.2 使用方法
第一種:配置文件方式
①編寫(xiě)作業(yè)類
即普通的pojo捌议,如下:
import org.springframework.stereotype.Service;
@Service
public class TaskJob {
public void job1() {
System.out.println(“任務(wù)進(jìn)行中。引有。瓣颅。”);
}
}
②在spring配置文件頭中添加命名空間及描述
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:task="http://www.springframework.org/schema/task"
……
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
③Spring配置文件中設(shè)置具體的任務(wù)
<task:scheduled-tasks>
<task:scheduled ref="taskJob" method="job1" cron="0 * * * * ?"/>
</task:scheduled-tasks>
<context:component-scan base-package=" com.gy.mytask " />
說(shuō)明:ref參數(shù)指定的即任務(wù)類譬正,method指定的即需要運(yùn)行的方法宫补,cron及cronExpression表達(dá)式,具體寫(xiě)法這里不介紹了曾我,詳情見(jiàn)附錄粉怕。
<context:component-scan base-package="com.gy.mytask" />這個(gè)配置根據(jù)項(xiàng)目實(shí)際情況調(diào)整包的位置,spring掃描注解用的抒巢。
第二種:使用注解形式
①編寫(xiě)作業(yè)類
即普通的pojo贫贝,如下:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component(“taskJob”)
public class TaskJob {
@Scheduled(cron = "0 0 3 * * ?")
public void job1() {
System.out.println(“任務(wù)進(jìn)行中。蛉谜。稚晚〕缍拢”);
}
}
注意:此處@Schedule注解有三個(gè)方法或者叫參數(shù),分別表示的意思是:
cron:指定cron表達(dá)式
fixedDelay:即表示從上一個(gè)任務(wù)完成開(kāi)始到下一個(gè)任務(wù)開(kāi)始的間隔蜈彼,單位是毫秒筑辨。
fixedRate:即從上一個(gè)任務(wù)開(kāi)始到下一個(gè)任務(wù)開(kāi)始的間隔,單位是毫秒幸逆。
②添加task相關(guān)的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"
default-lazy-init="false">
<context:annotation-config />
<!—spring掃描注解的配置 -->
<context:component-scan base-package="com.gy.mytask" />
<!—開(kāi)啟這個(gè)配置棍辕,spring才能識(shí)別@Scheduled注解 -->
<task:annotation-driven scheduler="qbScheduler" mode="proxy"/>
<task:scheduler id="qbScheduler" pool-size="10"/>
說(shuō)明:理論上只需要加上《task:annotation-driven /》這句配置就可以了,這些參數(shù)都不是必須的还绘。
2.3 定時(shí)任務(wù)框架Quartz
2.3.1 簡(jiǎn)介
Quartz是OpenSymphony開(kāi)源組織在Job scheduling領(lǐng)域又一個(gè)開(kāi)源項(xiàng)目楚昭,它可以與J2EE與J2SE應(yīng)用程序相結(jié)合也可以單獨(dú)使用。Quartz可以用來(lái)創(chuàng)建簡(jiǎn)單或?yàn)檫\(yùn)行十個(gè)拍顷,百個(gè)抚太,甚至是好幾萬(wàn)個(gè)Jobs這樣復(fù)雜的程序。Jobs可以做成標(biāo)準(zhǔn)的Java組件或 EJBs昔案。Quartz的最新版本為Quartz 2.2.2尿贫。下面將介紹兩種Quartz使用方式:
2.3.2 使用方法
第一種:作業(yè)類繼承自特定的基類
org.springframework.scheduling.quartz.QuartzJobBean
①編寫(xiě)作業(yè)類
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class Job1 extends QuartzJobBean {
private int timeout;
private static int i = 0;
//調(diào)度工廠實(shí)例化后,經(jīng)過(guò)timeout時(shí)間開(kāi)始執(zhí)行調(diào)度
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* 要調(diào)度的具體任務(wù)
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("定時(shí)任務(wù)執(zhí)行中…");
}
}
②spring配置文件中配置作業(yè)類JobDetailBean
<bean name="job1" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.gy.Job1" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="0" />
</map>
</property>
</bean>
說(shuō)明:org.springframework.scheduling.quartz.JobDetailBean有兩個(gè)屬性踏揣,jobClass屬性即我們?cè)趈ava代碼中定義的任務(wù)類庆亡,jobDataAsMap屬性即該任務(wù)類中需要注入的屬性值。
③配置作業(yè)調(diào)度的觸發(fā)方式(觸發(fā)器)
Quartz的作業(yè)觸發(fā)器有兩種捞稿,分別是
org.springframework.scheduling.quartz.SimpleTriggerBean
org.springframework.scheduling.quartz.CronTriggerBean
第一種SimpleTriggerBean又谋,只支持按照一定頻度調(diào)用任務(wù),如每隔30分鐘運(yùn)行一次娱局。
配置方式如下:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="job1" />
<!-- 調(diào)度工廠實(shí)例化后彰亥,經(jīng)過(guò)0秒開(kāi)始執(zhí)行調(diào)度 -->
<property name="startDelay" value="0" />
<!-- 每2秒調(diào)度一次 -->
<property name="repeatInterval" value="2000" />
</bean>
第二種CronTriggerBean,支持到指定時(shí)間運(yùn)行一次衰齐,如每天12:00運(yùn)行一次等任斋。
配置方式如下:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job1" />
<!—每天12:00運(yùn)行一次 -->
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
④配置調(diào)度工廠
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
說(shuō)明:該參數(shù)指定的就是之前配置的觸發(fā)器的名字。
⑤啟動(dòng)你的應(yīng)用即可娇斩,即將工程部署至tomcat或其他容器仁卷。
第二種:作業(yè)類不繼承特定基類(推薦使用)
Spring能夠支持這種方式,歸功于兩個(gè)類:
org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
這兩個(gè)類分別對(duì)應(yīng)spring支持的兩種實(shí)現(xiàn)任務(wù)調(diào)度的方式犬第,即前文提到到j(luò)ava自帶的timer task方式和Quartz方式锦积。這里我只寫(xiě)MethodInvokingJobDetailFactoryBean的用法,使用該類的好處是,我們的任務(wù)類不再需要繼承自任何類歉嗓,而是普通的pojo丰介。
①編寫(xiě)作業(yè)類(普通POJO)
public class Job2 {
public void doJob2() {
System.out.println("不繼承QuartzJobBean方式-調(diào)度進(jìn)行中...");
}
}
②配置作業(yè)類
<bean id="job2"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="com.gy.Job2" />
</property>
<property name="targetMethod" value="doJob2" />
<!-- 作業(yè)不并發(fā)調(diào)度 -->
<property name="concurrent" value="false" />
</bean>
說(shuō)明:這一步是關(guān)鍵步驟,聲明一個(gè)MethodInvokingJobDetailFactoryBean,有兩個(gè)關(guān)鍵屬性:targetObject指定任務(wù)類哮幢,targetMethod指定運(yùn)行的方法带膀。
③配置作業(yè)調(diào)度的觸發(fā)方式(觸發(fā)器)
Quartz的作業(yè)觸發(fā)器有兩種,分別是
org.springframework.scheduling.quartz.SimpleTriggerBean
org.springframework.scheduling.quartz.CronTriggerBean
第一種SimpleTriggerBean橙垢,只支持按照一定頻度調(diào)用任務(wù)垛叨,如每隔30分鐘運(yùn)行一次。
配置方式如下:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="job1" />
<!-- 調(diào)度工廠實(shí)例化后柜某,經(jīng)過(guò)0秒開(kāi)始執(zhí)行調(diào)度 -->
<property name="startDelay" value="0" />
<!-- 每2秒調(diào)度一次 -->
<property name="repeatInterval" value="2000" />
</bean>
第二種CronTriggerBean嗽元,支持到指定時(shí)間運(yùn)行一次,如每天12:00運(yùn)行一次等喂击。
配置方式如下:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job1" />
<!—每天12:00運(yùn)行一次 -->
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
④配置調(diào)度工廠
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
說(shuō)明:該參數(shù)指定的就是之前配置的觸發(fā)器的名字剂癌。
⑤啟動(dòng)你的應(yīng)用即可,即將工程部署至tomcat或其他容器翰绊。
2.2.3 擴(kuò)展內(nèi)容
①配置多個(gè)定時(shí)器任務(wù)
<util:properties id="applicationProps" location="classpath:enroll.properties" />
<context:property-placeholder properties-ref="applicationProps" />
<bean id="waitListExpireJob" class="com.yunteng.ngtl.enroll.tool.waitListExpireTaskJob" />
<bean id="expireJobTask"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="waitListExpireJob" />
<property name="targetMethod" value="expireProcess" />
</bean>
<bean id="waitListExpireTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="name" value="waitListExpireTriggerName" />
<property name="group" value="waitListExpireTriggerGroup" />
<property name="jobDetail">
<ref bean="expireJobTask" />
</property>
<property name="cronExpression">
<value>#{applicationProps['cron.expireTask']}</value>
</property>
</bean>
<bean id="waitListObserveJob" class="com.yunteng.ngtl.enroll.tool.waitListObserveTaskJob" />
<bean id="observeJobTask"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="waitListObserveJob" />
<property name="targetMethod" value="observeProcess" />
</bean>
<bean id="waitListObserveTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="name" value="waitListObserveTriggerName" />
<property name="group" value="waitListObserveTriggerGroup" />
<property name="jobDetail">
<ref bean="observeJobTask" />
</property>
<property name="cronExpression">
<value>#{applicationProps['cron.observeTask']}</value>
</property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="waitListExpireTrigger" />
<ref local="waitListObserveTrigger" />
</list>
</property>
</bean>
②動(dòng)態(tài)修改定時(shí)器任務(wù)調(diào)度時(shí)間周期(關(guān)鍵字:quartz change cron expression runtime)
注意:目前只在Quartz1.8.6版本下測(cè)試成功
CronTrigger cronTrigger = (CronTrigger) stdScheduler.getTrigger(triggerName,triggerGroupName);
cronTrigger.setCronExpression(newCronExpression);
stdScheduler.rescheduleJob(triggerName,triggerGroupName,cronTrigger);
3 總結(jié)與分析
~ | Timer | Spring-Task | Quartz |
---|---|---|---|
作業(yè)類的繼承方式 | java.util.Timer中需要繼承自java.util.TimerTask | 普通的java類佩谷,不需要繼承其他類 | 繼承自org.springframework.scheduling.quartz.QuartzJobBean |
是否可以使用Cron表達(dá)式 | 不可以 | 可以 | 可以 |
動(dòng)態(tài)改變執(zhí)行時(shí)間周期 | 可以,但是使用不靈活 | 資料太少监嗜,未找到相關(guān)方法 | 可以谐檀,將觸發(fā)器重新啟動(dòng)即可重新調(diào)度任務(wù)(資料較多,目前只在Quartz1.8.6版本測(cè)試通過(guò)) |
使用難易程度 | 簡(jiǎn)單 | 簡(jiǎn)單 | 稍難裁奇,需要配置的部分相對(duì)較多且繁瑣 |
這篇博文到這里就結(jié)束了稚补,希望下次有時(shí)間可以多添加一些圖表等更加形象的內(nèi)容。