上次熟悉了MarkDown的用法之后,由于各种原因一直没有时间更新博客。。。这次打算把我之前总结的一些东西陆陆续续的写在博客里,希望下次用到的时候能够快速记起来~
---------------------------------------华丽的分割线--------------------------------------------
1 定时任务简介
在应用开发中,经常需要一些周期性的操作,如:需要在每天凌晨时候分析一次前一天的日志信息、需要每隔5分钟检查一下某个模块是否有异常然后自动发送邮件给管理员,在项目运行到第30天的时候需要执行某些操作等等。这些功能需求就需要我们使用一些定时任务方法去实现,本文将介绍目前J2EE项目常用的几种定时任务方法并比较它们的优缺点。
2 J2EE项目中常用到的三种定时任务实现方法
2.1 java.util.Timer类
2.1.1 简介
先来介绍下Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。TimerTask类用于实现由Timer安排的一次或重复执行的某个任务。每一个Timer对象对应的是一个线程,因此计时器所执行的任务应该迅速完成,否则会延迟后续的任务执行。
java.util.Timer类方法摘要
void cancel()
终止此计时器,丢弃所有当前已安排的任务。
int purge()
从此计时器的任务队列中移除所有已取消的任务。
void schedule(TimerTask task, Date time)
安排在指定的时间执行指定的任务。
void schedule(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定延迟执行。
void schedule(TimerTask task, long delay)
安排在指定延迟后执行指定的任务。
void schedule(TimerTask task, long delay, long period)
安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定速率执行。
void scheduleAtFixedRate(TimerTask task, long delay, long period)
安排指定的任务在指定的延迟后开始进行重复的固定速率执行。
TimerTask类方法摘要
boolean cancel()
取消此计时器任务。
abstract void run()
此计时器任务要执行的操作。
long scheduledExecutionTime()
返回此任务最近实际执行的安排执行时间。
2.1.2 使用方法
使用Timer类的schedule(TimerTask task, long delay, long period)方法启动定时器。
Timer timer=new Timer();
MyTask myTask=new MyTask();
timer.schedule(myTask, 1000, 2000);
TimerTask类主要实现run()方法里的业务逻辑,用法如下:
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("当前的系统时间为:"+simpleDateFormat.format(new Date()));
}
}
2.1.3 扩展内容(往定时任务方法中传参数)
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() {
// 参数waitList使用示例
List<CourseWaitList> allCourseWaitList = this.waitList.getAllCourseWaitList();
}
}
2.2 Spring3.0以后自带的Spring-task
2.2.1 简介
Spring3.0以后自主开发了定时任务工具spring task,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种形式,下面将分别介绍这两种方式。
2.2.2 使用方法
第一种:配置文件方式
①编写作业类
即普通的pojo,如下:
import org.springframework.stereotype.Service;
@Service
public class TaskJob {
public void job1() {
System.out.println(“任务进行中。。。”);
}
}
②在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配置文件中设置具体的任务
<task:scheduled-tasks>
<task:scheduled ref="taskJob" method="job1" cron="0 * * * * ?"/>
</task:scheduled-tasks>
<context:component-scan base-package=" com.gy.mytask " />
说明:ref参数指定的即任务类,method指定的即需要运行的方法,cron及cronExpression表达式,具体写法这里不介绍了,详情见附录。
<context:component-scan base-package="com.gy.mytask" />这个配置根据项目实际情况调整包的位置,spring扫描注解用的。
第二种:使用注解形式
①编写作业类
即普通的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(“任务进行中。。。”);
}
}
注意:此处@Schedule注解有三个方法或者叫参数,分别表示的意思是:
cron:指定cron表达式
fixedDelay:即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。
fixedRate:即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。
②添加task相关的配置
<?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" />
<!—开启这个配置,spring才能识别@Scheduled注解 -->
<task:annotation-driven scheduler="qbScheduler" mode="proxy"/>
<task:scheduler id="qbScheduler" pool-size="10"/>
说明:理论上只需要加上《task:annotation-driven /》这句配置就可以了,这些参数都不是必须的。
2.3 定时任务框架Quartz
2.3.1 简介
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.2.2。下面将介绍两种Quartz使用方式:
2.3.2 使用方法
第一种:作业类继承自特定的基类
org.springframework.scheduling.quartz.QuartzJobBean
①编写作业类
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;
//调度工厂实例化后,经过timeout时间开始执行调度
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* 要调度的具体任务
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("定时任务执行中…");
}
}
②spring配置文件中配置作业类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>
说明:org.springframework.scheduling.quartz.JobDetailBean有两个属性,jobClass属性即我们在java代码中定义的任务类,jobDataAsMap属性即该任务类中需要注入的属性值。
③配置作业调度的触发方式(触发器)
Quartz的作业触发器有两种,分别是
org.springframework.scheduling.quartz.SimpleTriggerBean
org.springframework.scheduling.quartz.CronTriggerBean
第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。
配置方式如下:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="job1" />
<!-- 调度工厂实例化后,经过0秒开始执行调度 -->
<property name="startDelay" value="0" />
<!-- 每2秒调度一次 -->
<property name="repeatInterval" value="2000" />
</bean>
第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。
配置方式如下:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job1" />
<!—每天12:00运行一次 -->
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
④配置调度工厂
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
说明:该参数指定的就是之前配置的触发器的名字。
⑤启动你的应用即可,即将工程部署至tomcat或其他容器。
第二种:作业类不继承特定基类(推荐使用)
Spring能够支持这种方式,归功于两个类:
org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
这两个类分别对应spring支持的两种实现任务调度的方式,即前文提到到java自带的timer task方式和Quartz方式。这里我只写MethodInvokingJobDetailFactoryBean的用法,使用该类的好处是,我们的任务类不再需要继承自任何类,而是普通的pojo。
①编写作业类(普通POJO)
public class Job2 {
public void doJob2() {
System.out.println("不继承QuartzJobBean方式-调度进行中...");
}
}
②配置作业类
<bean id="job2"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="com.gy.Job2" />
</property>
<property name="targetMethod" value="doJob2" />
<!-- 作业不并发调度 -->
<property name="concurrent" value="false" />
</bean>
说明:这一步是关键步骤,声明一个MethodInvokingJobDetailFactoryBean,有两个关键属性:targetObject指定任务类,targetMethod指定运行的方法。
③配置作业调度的触发方式(触发器)
Quartz的作业触发器有两种,分别是
org.springframework.scheduling.quartz.SimpleTriggerBean
org.springframework.scheduling.quartz.CronTriggerBean
第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。
配置方式如下:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="job1" />
<!-- 调度工厂实例化后,经过0秒开始执行调度 -->
<property name="startDelay" value="0" />
<!-- 每2秒调度一次 -->
<property name="repeatInterval" value="2000" />
</bean>
第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。
配置方式如下:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job1" />
<!—每天12:00运行一次 -->
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
④配置调度工厂
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
说明:该参数指定的就是之前配置的触发器的名字。
⑤启动你的应用即可,即将工程部署至tomcat或其他容器。
2.2.3 扩展内容
①配置多个定时器任务
<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>
②动态修改定时器任务调度时间周期(关键字:quartz change cron expression runtime)
注意:目前只在Quartz1.8.6版本下测试成功
CronTrigger cronTrigger = (CronTrigger) stdScheduler.getTrigger(triggerName,triggerGroupName);
cronTrigger.setCronExpression(newCronExpression);
stdScheduler.rescheduleJob(triggerName,triggerGroupName,cronTrigger);
3 总结与分析
~ | Timer | Spring-Task | Quartz |
---|---|---|---|
作业类的继承方式 | java.util.Timer中需要继承自java.util.TimerTask | 普通的java类,不需要继承其他类 | 继承自org.springframework.scheduling.quartz.QuartzJobBean |
是否可以使用Cron表达式 | 不可以 | 可以 | 可以 |
动态改变执行时间周期 | 可以,但是使用不灵活 | 资料太少,未找到相关方法 | 可以,将触发器重新启动即可重新调度任务(资料较多,目前只在Quartz1.8.6版本测试通过) |
使用难易程度 | 简单 | 简单 | 稍难,需要配置的部分相对较多且繁琐 |
这篇博文到这里就结束了,希望下次有时间可以多添加一些图表等更加形象的内容。