定时任务实现方法总结与分析

上次熟悉了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指定的即需要运行的方法,croncronExpression表达式,具体写法这里不介绍了,详情见附录。
<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版本测试通过)
使用难易程度 简单 简单 稍难,需要配置的部分相对较多且繁琐

这篇博文到这里就结束了,希望下次有时间可以多添加一些图表等更加形象的内容。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,097评论 18 139
  • 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息,借此机会整理了一下定时任务的...
    低至一折起阅读 487评论 0 3
  • Timer 定时器相信都不会陌生,之所以拿它来做源码分析,是发现整个控制流程可以体现很多有意思的东西。 在业务开发...
    石先阅读 6,216评论 2 13
  • 我是紫君 我的原创 1 昨天晚上和老友张华、王丽一起在《外婆家》吃饭,饭到中途,张华摇摇头,意味深长的感叹道:“哎...
    昭洁紫君阅读 284评论 1 0
  • 喜欢三毛这个女人,生性浪漫,性格洒脱不羁,却又重感情。我以为即使她只是一片流浪的树叶,在流浪的途中,也不会忘记生养...
    第九街人阅读 257评论 0 0