- 记录几种定时器的实现方式——仅仅在应用层面,简单的实现。本文在完成过程中参考了详解java定时任务、Spring定时任务的几种实现等文章。
实现定时器的几种方式
- java.util.Timer与java.util.TimerTask。个人总结了一下,这种方式因为有一些缺陷,适合一些简单的定时任务。参考文章详解java定时任务,非常详细。
- Spring与Quartz的结合,配置比较麻烦,公司的老项目还在用这种方式。
- Spring3.0以上版本自带的task,配置简单,新项目使用的这种方式。
两种部署方式(linux,项目使用spring的情况下)
- 如果不是web工程,编写一个启动类,用于加载spring的配置文件启动spring容器,并编写启动java程序的脚本(参考不错的linux下通用的java程序启动脚本)。
- 如果是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包版本和配置不一致。
4.0.2.RELEASE
false
org.quartz-scheduler
quartz
2.1.1
org.springframework
spring-core
${spring.version}
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配置:
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' 的声明。
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开启注解扫描
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");
}
}
执行结果:
至此,三种定时方式简单实现了一遍,更多的配置参数,以及效率、性能在此未作深入研究。