主要有以下三种实现方式:
JDK自带:JDK 自带的 Timer 以及 JDK1.5+ 新增的 ScheduledExecutorService;
Quartz:简单却强大的 JAVA 作业调度框架;
Spring3.0以后自带的task :可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多;
JDK 自带的定时器实现
Timer
这个类允许你调度一个java.util.TimerTask任务。主要有以下几个方法:
schedule(TimerTask task, long delay) 延迟 delay 毫秒执行
schedule(TimerTask task, Date time) 特定时间执行
schedule(TimerTask task, long delay, long period) 延迟 delay 执行并每隔 period 执行一次。用固定延迟调度。使用本方法时,在任务执行中的每一个延迟会传播到后续的任务的执行。
scheduleAtFixedRate(TimerTask task, long delay, long period) 延迟 delay 执行并固定速率 period 执行一次。用固定比率调度。使用本方法时,所有后续执行根据初始执行的时间进行调度,从而希望减小延迟。
ScheduledExecutorService
该定时任务接口,主要有以下几个方法
ScheduledFuture> schedule(Runnable command,long delay, TimeUnit unit);
ScheduledFutureschedule(Callablecallable,long delay, TimeUnit unit);
ScheduledFuture> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);
ScheduledFuture> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);
该接口的默认实现为 ScheduledThreadPoolExecutor 类,这个类继承了 ThreadPoolExecutor 类。线程池的使用使其比Timer更稳定。spring Task内部也是依靠它实现的。
Timer的缺陷
1、Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,后面的任务执行时间就被推迟。
packagecn.boomoom.service;importjava.util.Timer;importjava.util.TimerTask;public classTimerTest {private static longstart;public static voidmain(String[] args) {
TimerTask task1= newTimerTask() {
@Overridepublic voidrun() {
System.out.println("task1 invoked ! " + (System.currentTimeMillis() -start));try{
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
};
TimerTask task2= newTimerTask() {
@Overridepublic voidrun() {
System.out.println("task2 invoked ! " + (System.currentTimeMillis() -start));
}
};
Timer timer= newTimer();
start=System.currentTimeMillis();
timer.schedule(task1,1000);
timer.schedule(task2,3000);
}
}
TimerTest
定义了两个任务,预计是第一个任务1s后执行,第二个任务3s后执行,但是看运行结果:
task1 invoked ! 1000task2 invoked! 4001
task2实际上是4后才执行,正因为Timer内部是一个线程,而任务1所需的时间超过了两个任务间的间隔导致。
packagecn.boomoom.service;importjava.util.TimerTask;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;public classScheduledExecutorServiceTest {private static longstart;public static voidmain(String[] args) {//使用工厂方法初始化一个ScheduledThreadPool
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
TimerTask task1= newTimerTask() {
@Overridepublic voidrun() {try{
System.out.println("task1 invoked ! " + (System.currentTimeMillis() -start));
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
}
};
TimerTask task2= newTimerTask() {
@Overridepublic voidrun() {
System.out.println("task2 invoked ! " + (System.currentTimeMillis() -start));
}
};
start=System.currentTimeMillis();
newScheduledThreadPool.schedule(task1,1000, TimeUnit.MILLISECONDS);
newScheduledThreadPool.schedule(task2,3000, TimeUnit.MILLISECONDS);
}
}
ScheduledExecutorServiceTest
task1 invoked ! 1002task2 invoked! 3004
符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行。
2、Timer当任务抛出异常时的缺陷。如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。
packagecn.boomoom.service;importjava.util.Date;importjava.util.Timer;importjava.util.TimerTask;public classTimerTest01 {public static voidmain(String[] args) {final TimerTask task1 = newTimerTask() {
@Overridepublic voidrun() {throw newRuntimeException();
}
};final TimerTask task2 = newTimerTask() {
@Overridepublic voidrun() {
System.out.println("task2 invoked!");
}
};
Timer timer= newTimer();
timer.schedule(task1,100);
timer.scheduleAtFixedRate(task2,new Date(), 1000);
}
}
TimerTest01
task2 invoked!Exception in thread"Timer-0"java.lang.RuntimeException
at cn.itcast.bos.service.TimerTest01$1.run(TimerTest01.java:14)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
由于任务1的一次,任务2也停止运行了。
packagecn.boomoom.service;importjava.util.TimerTask;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;public classScheduledExecutorServiceTest01 {public static voidmain(String[] args) {final TimerTask task1 = newTimerTask() {
@Overridepublic void run() { throw newRuntimeException();}
};final TimerTask task2 = newTimerTask() {
@Overridepublic void run() { System.out.println("task2 invoked!"); }
};
ScheduledExecutorService pool= Executors.newScheduledThreadPool(1);
pool.schedule(task1,100, TimeUnit.MILLISECONDS);
pool.scheduleAtFixedRate(task2,0, 1000, TimeUnit.MILLISECONDS);
}
}
ScheduledExecutorServiceTest01
task2 持续输出。ScheduledExecutorService 保证了,task1出现异常时,不影响task2的运行。
3、Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService 基于时间的延迟,不会由于系统时间的改变发生执行变化。
Quartz 任务调度实现
quartz 的 java demo
http://www.quartz-scheduler.org/ 入门案例: http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html
1、导入maven坐标
4.0.0
cn.boomoom.maven
quartz_helloworld
0.0.1-SNAPSHOT
quartz_helloworld
org.quartz-scheduler
quartz
2.2.1
org.quartz-scheduler
quartz-jobs
2.2.1
org.slf4j
slf4j-log4j12
1.7.12
pom
2、quartz demo
packagecn.boomoom.service;import org.quartz.*;importorg.quartz.impl.StdSchedulerFactory;importjava.util.HashMap;importjava.util.Map;public classQuartzTest {public static void main(String[] args) throwsSchedulerException {
SchedulerFactory sf= newStdSchedulerFactory();
Scheduler scheduler=sf.getScheduler();
Map jobTriggerMap =jobAndTrigger();for (Map.Entryentry : jobTriggerMap.entrySet()) {
scheduler.scheduleJob(entry.getKey(), entry.getValue());
}
scheduler.start();
}private static MapjobAndTrigger() {
HashMap hashMap = new HashMap<>();
JobDetail job1= JobBuilder.newJob(HelloJob1.class).withIdentity("job1", "group1").build();
Trigger trigger1= TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
hashMap.put(job1, trigger1);
JobDetail job2= JobBuilder.newJob(HelloJob2.class).withIdentity("job2", "group1").build();
Trigger trigger2= TriggerBuilder.newTrigger().forJob("job2", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)).build();
hashMap.put(job2, trigger2);
JobDetail job3= JobBuilder.newJob(HelloJob3.class).withIdentity("job3", "group1").build();
Trigger trigger3= TriggerBuilder.newTrigger().forJob("job3", "group1").startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
hashMap.put(job3,trigger3);returnhashMap;
}
}
quartzTest
JobDetail、Trigger、Scheduler三个对象为Quartz主要对象。启动Trigger的定时方式由不同的ScheduleBuilder子类提供,如:SimpleScheduleBuilder、CronScheduleBuilder、DailyTimeIntervalScheduleBuilder、CalendarIntervalScheduleBuilder。
spring 集成 quartz
4.0.0
cn.boomoom.maven
quartz_spring
0.0.1-SNAPSHOT
war
quartz_spring
org.quartz-scheduler
quartz
2.2.1
org.quartz-scheduler
quartz-jobs
2.2.1
org.slf4j
slf4j-log4j12
1.7.12
org.springframework
spring-context
4.1.7.RELEASE
org.springframework
spring-context-support
4.1.7.RELEASE
org.springframework
spring-web
4.1.7.RELEASE
org.springframework
spring-tx
4.1.7.RELEASE
org.codehaus.mojo
tomcat-maven-plugin
1.1
9888
1、pom.xml
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
2、web.xml
packagecn.boomoom.job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.scheduling.quartz.QuartzJobBean;importcn.boomoom.service.HelloService;public class HelloJob extendsQuartzJobBean {
@AutowiredprivateHelloService helloService;
@Overrideprotected void executeInternal(JobExecutionContext context) throwsJobExecutionException {
helloService.sayHello();
}
}
HelloJob
packagecn.boomoom.service;importorg.springframework.stereotype.Service;
@Servicepublic classHelloService {public voidsayHello() {
System.out.println("hello,quartz service !");
}
}
HelloService
packagecn.boomoom.job;importorg.quartz.spi.TriggerFiredBundle;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.config.AutowireCapableBeanFactory;importorg.springframework.scheduling.quartz.AdaptableJobFactory;importorg.springframework.stereotype.Service;
@Service("jobFactory")public class JobFactory extendsAdaptableJobFactory {
@AutowiredprivateAutowireCapableBeanFactory capableBeanFactory;
@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throwsException {//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);//进行注入
capableBeanFactory.autowireBean(jobInstance);returnjobInstance;
}
}
JobFactory
4、applicationContext.xml
spring提供了JobDetail、Trigger、Scheduler三个的factoryBean。
在 Job 中 spring 管理的 Bean 无法注入解决,需要在 Scheduler 中自定义 JobFactory。jobFactory 在创建job对象的时候,为其指定了 spring自动注入的属性,(类中的@Autowired)。
JobFactory 指定定义的时候,上面的方式有些时候不行,可以用下面的方式
importorg.quartz.spi.TriggerFiredBundle;importorg.springframework.beans.factory.config.AutowireCapableBeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.scheduling.quartz.SpringBeanJobFactory;
@Service("jobFactory")public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implementsApplicationContextAware {private transientAutowireCapableBeanFactory beanFactory;
@Overridepublic void setApplicationContext(finalApplicationContext context) {
beanFactory=context.getAutowireCapableBeanFactory();
}
@Overrideprotected Object createJobInstance(final TriggerFiredBundle bundle) throwsException {final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);returnjob;
}
}
AutowiringSpringBeanJobFactory
JobFactory 参考1,参考2. quartz定时任务实现只执行一次,SimpleTriggerFactoryBean
Spring 相关的任务调度
Spring 3.0+ 自带的任务调度实现,主要依靠TaskScheduler接口的几个实现类实现。
主要用法有以下三种:
配置文件实现
spring-schedule.xml
注解实现
spring-schedule.xml
// 启用注解
@Componentpublic classScheduleTask {//每隔5秒执行一次
@Scheduled(cron = "0/5 * * * * ?")public voidprintSay() {
System.out.println("每隔5秒执行一次:" + newDate());
}
}
代码动态添加
spring-schedule.xml
@Componentpublic classTest {
@AutowiredprivateThreadPoolTaskScheduler myScheduler;public voidaddJob(){
myScheduler.schedule(newRunnable() {
@Overridepublic voidrun() {
System.out.println(Thread.currentThread().getName()+ " run ");
}
} ,new CronTrigger("0/5 * * * * ? ")); //每5秒执行一次
}
}