Java定时器,Spring定时器


实现定时器的几种方式

  1. java.util.Timer与java.util.TimerTask。个人总结了一下,这种方式因为有一些缺陷,适合一些简单的定时任务。参考文章详解java定时任务,非常详细。
  2. Spring与Quartz的结合,配置比较麻烦,公司的老项目还在用这种方式。
  3. Spring3.0以上版本自带的task,配置简单,新项目使用的这种方式。

两种部署方式(linux,项目使用spring的情况下)

  1. 如果不是web工程,编写一个启动类,用于加载spring的配置文件启动spring容器,并编写启动java程序的脚本(参考不错的linux下通用的java程序启动脚本)。
  2. 如果是web工程,将spring加进web.xml的监听器,随着web工程的启动而启动。

实例

一、java.util.Timer与java.util.TimerTask简单实例。

1、创建任务类
package org.my.timertask;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
/**
 * 创建任务类,继承TimerTask,重写其run方法,方法体指定要执行的任务。
 * @author fasen
 *
 */
public class TimerTaskDemo extends TimerTask{
  @Override
  public void run() {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
      System.out.println("这是java.util.TimeTask在执行任务,本次执行时间是:"+sdf.format(new Date()));
  }
}
2、使用调度对象进行调用
package org.my.timertask;
import java.util.Timer;
public class TimerDemo {
  public static void main(String[] args) {
      //创建Timer对象,并调用schedule方法(有四个重载方法)
      Timer t = new Timer();
      //传递参数。第一个参数是TimerTask对象(指定要执行的任务),第二个参数是延迟时间,第三个参数是时间间隔(毫秒)
      t.schedule(new TimerTaskDemo(),0,5000);
  }
}

运行结果

运行结果
3、 缺陷

Timer在执行所有定时任务时只会创建一个线程,如果某个任务的执行时间过长,那么将破坏其他TimerTask的定时精确性。例如某个周期TimerTask需要每10ms执行一次,而另一个TimerTask需要执行40ms,那么这个周期任务会在40ms任务执行完成后快速连续地调用4次或者彻底丢失这4次调用(取决于它是基于固定频率来调度还是固定延时来调度。)

Timer的另一个问题是,如果TimerTask抛出了一个未检查的异常,那么Timer线程不会捕获异常,此时会终止定时线程,并且不会恢复。那么整个Timer就被错误的取消了(摘自《Java并发编程实战》)。

二、Spring与Quartz的结合使用

1、导入jar包

org.quartz-scheduler,我使用的是2.1.1。我用的Spring的版本是Spring4.0.2,如果使用quartz2.x与Spring3.1以上的版本,配置的bean类必须与本配置一致。根据Spring和quart的版本不同,会有不同的配置。quartz2.x与Spring3.1以上的版本将老版本很多类名加了Factory,比如将CronTriggerBean修改为CronTriggerFactoryBean、将JobDetailBean修改为JobDetailFactoryBean等等,如果用quartz2.x与Spring3.1以上的版本的jar,而使用低版本的配置——CronTriggerBean和JobDetailBean话,会出现类似以下的错误

class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as super class

这个错误,即代表jar包版本和配置不一致。

<properties>  
      <!-- spring版本号 -->  
      <spring.version>4.0.2.RELEASE</spring.version> 
      <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties> 
<!-- spring核心包 --> 
<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.1.1</version>
    </dependency>
 <!-- spring核心包 -->  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-core</artifactId>  
        <version>${spring.version}</version>  
    </dependency> 
2、编写任务类

Spring与Quartz的结合使用,其任务类有两种方式——任务类继承QuartzJobBean类、任务类不继承特定类。

任务类继承QuartzJobBean类,需要重写executeInternal方法:

package org.my.SpringQuartz;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class SpringQuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { 
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("这是SpringQuartzJob定时任务...任务类继承QuartzJobBean,当前时间:"+sdf.format(new Date()));  
    }
}

任务类不继承特定类,POJO,方法名自定义:

package org.my.SpringQuartz;
import java.text.SimpleDateFormat;
import java.util.Date;
public class NotExtendSpringQuartzJob {
    public void dosomething(){
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("这是NotExtendSpringQuartzJob定时任务...任务类不继承特定类,是POJO,现在时间:"+sdf.format(new Date()));
    }
}
3、spring配置:
<?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:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  
<!-- 定时任务配置(任务类继承特定的类) 开头 -->
    <!-- 定时任务bean,任务执行类 -->
    <bean name="springQuartzJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" >  
        <property name="jobClass" value="org.my.SpringQuartz.SpringQuartzJob" />  
        <!-- 任务类的属性map,可以设置参数,我这里没有属性 -->
        <!-- <property name="jobDataAsMap">  
            <map>  
            <entry key="timeout" value="0" />  
            </map>  
        </property>   -->
    </bean> 
    <!-- 任务执行类对应的触发器:第一种(按照一定的时间间隔触发) -->
    <bean name="springQuartzJobTrigger1" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="springQuartzJob" />  
        <property name="startDelay" value="0" /><!-- 调度工厂实例化后,经过0秒开始执行调度 -->  
        <property name="repeatInterval" value="30000" /><!-- 每30秒调度一次 -->  
    </bean>
    <!-- 任务执行类对应的触发器:第二种(按照指定时间触发,如每天12点) -->
    <bean name="springQuartzJobTrigger2" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="springQuartzJob" />  
        <!-- 每天21:37运行一次 -->  
        <property name="cronExpression" value="0 37 21 * * ?" />  
    </bean>
    <!-- 调度工厂 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
        <list>  
            <ref bean="springQuartzJobTrigger1" />  
            <ref bean="springQuartzJobTrigger2" />
        </list>  
        </property>  
    </bean> 
<!-- 定时任务配置(任务类继承特定的类) 结尾 -->   
<!-- 定时任务配置(任务类不继承特定的类)开头 -->
    <!-- 定时任务bean,任务执行类 -->
     <bean id="notExtendSpringQuartzJob"  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">  <!-- 指定任务类 -->
            <bean class="org.my.SpringQuartz.NotExtendSpringQuartzJob" />  
        </property>  
        <property name="targetMethod" value="dosomething" />  <!-- 制定任务类中的方法 -->
        <property name="concurrent" value="false" /><!-- 作业不并发调度 -->  
    </bean> 
    <!-- 任务执行类对应的触发器:第一种(按照一定的时间间隔触发) -->
    <bean name="notExtendSpringQuartzJobTrigger1" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="notExtendSpringQuartzJob" />  
        <property name="startDelay" value="0" /><!-- 调度工厂实例化后,经过0秒开始执行调度 -->  
        <property name="repeatInterval" value="30000" /><!-- 每30秒调度一次 -->  
    </bean>
    <!-- 任务执行类对应的触发器:第二种(按照指定时间触发,如每天12点) -->
    <bean name="notExtendSpringQuartzJobTrigger2" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="notExtendSpringQuartzJob" />  
        <!-- 每天21:37运行一次 -->  
        <property name="cronExpression" value="0 37 21 * * ?" />  
    </bean>
    <!-- 调度工厂 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
        <list>  
            <ref bean="notExtendSpringQuartzJobTrigger1" />  
            <ref bean="notExtendSpringQuartzJobTrigger2" />  
        </list>  
        </property>  
    </bean>
<!-- 定时任务配置(任务类不继承特定的类)结尾 --> 
</beans> 
4、启动。
package org.my.SpringQuartz;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StartSpringQuartz {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");  
    }
}

执行结果如下,红框外是根据指定时间间隔触发的——SimpleTriggerFactoryBean,红框内的是设定指定时间触发——CronTriggerFactoryBean,此处配置的是21:37分。

执行结果

三、Spring3.0以上版本自带的task。

配置文件方式:

1、编写任务类,POJO。
package org.my.SpringTask;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.stereotype.Component;
@Component
public class TaskJob {
    public void dosomething(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("这是SpringTask定时任务,当前时间:"+sdf.format(new Date()));
    }
}
2、Spring配置

applicationContext-SpringTask.xml,在头部文件中加入了task声明。如果没有声明,会出现类似以下的异常

org.xml.sax.SAXParseException; lineNumber: 19; columnNumber: 24; cvc-complex-type.2.4.c: 通配符的匹配很全面, 但无法找到元素 'task:scheduled-tasks' 的声明。
<?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:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                        http://www.springframework.org/schema/task
                        http://www.springframework.org/schema/task/spring-task-4.0.xsd">  
    <!-- 自动扫描(并启用注解)Spring-task定时方式可以用注解 -->  
    <context:component-scan base-package="org.my.SpringTask" />  

    <!-- Spring task定时配置 -->
    <task:scheduled-tasks>   
         <!-- 每15秒执行一次,此处可设置为以指定时间间隔或者指定的时间执行,具体由cron表达式决定 -->
            <task:scheduled ref="taskJob" method="dosomething" cron="0/15 * * * * ?"/>  
    </task:scheduled-tasks> 
</beans> 
3、启动
package org.my.SpringTask;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StartSpringTask {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-SpringTask.xml");
    }
}

运行结果:

运行结果

注解方式:

1、任务类:
package org.my.SpringTaskComment;

  import java.text.SimpleDateFormat;
  import java.util.Date;

  import org.springframework.scheduling.annotation.Scheduled;
  import org.springframework.stereotype.Component;
  @Component
  public class TaskJobComment {
    @Scheduled(cron="0/15 * * * * ?")//每15秒执行一次
    public void dosomething(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("这是SpringTask定时任务注解版,当前时  间:"+sdf.format(new Date()));
    }
}
2、Spring开启注解扫描
<?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:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                        http://www.springframework.org/schema/task
                        http://www.springframework.org/schema/task/spring-task-4.0.xsd">  
    <!-- 自动扫描(并启用注解)-->  
    <context:component-scan base-package="org.my" />  
  <!-- 开启这个扫描才能使用定时注解,这个配置项有一些非必需参数(属性),比如executor、scheduler、mode等等--> 
     <task:annotation-driven/>
</beans> 
3、启动
package org.my.SpringTaskComment;

 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;

 public class StartSpringTaskComment {

    public static void main(String[] args) {
        
        ApplicationContext ctx = new  ClassPathXmlApplicationContext("applicationContext-SpringTask.xml");

    }
}

执行结果:

执行结果

至此,三种定时方式简单实现了一遍,更多的配置参数,以及效率、性能在此未作深入研究。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • 上次熟悉了MarkDown的用法之后,由于各种原因一直没有时间更新博客。。。这次打算把我之前总结的一些东西陆陆续续...
    niaoge2016阅读 2,028评论 0 2
  • 最近接触了两概念,一个是说我们生活的这个世界是随机的。 比如我们扔出一个骰子,点数为1的概率是1/6;我们抛出一个...
    苗栋栋阅读 1,077评论 0 0
  • 第一次听到苏打绿这个名字的时候。那是大概在高一的时候,那时的自己还在想,怎么会有这么奇怪的一个名字,这一定是一个俗...
    猫腻在水中无法呼吸阅读 5,132评论 0 6