一、定时调度
定时调度指的是在一定时间条件下,系统自动完成特定功能的运行机制。传统的定时调度分为两种:
对于定时调度而言,所依赖的便是计算机系统底层的时钟发生器。在 Java 技术中,提供了两个操作类:Timer、TimerTask,其中,TimerTask 用于定义定时调度所要执行的任务和功能,线程运行时调用。
范例:
new Timer().scheduleAtFixedRate(new TimerTask() { private Logger logger = LoggerFactory.getLogger(TimerTask.class); @Override public void run() { logger.info(new Date().toString()); } }, new Date(), 2000);
复制代码
随着任务调度的复杂度提升,单纯地使用以上基础类进行编写已显得非常麻烦,因此,对于企业级项目而言,提供了以下两种方式实现任务调度。
Quartz:第三方的任务调度组件,需要进行配置;
Spring Task:spring 提供了任务调度模块,配置简单,可使用 annotation 实现。
二、Quartz
项目 pom.xml 文件配置 Quartz 组件。在实际开发中,Quartz 提供了两种实现方式:
直接继承父类实现;
配置方式实现任务调度,不需要继承父类。
建议不要使用直接继承父类的方式,这样会增加过多的硬编码,也同时增加了代码量,不利于维护。
一下介绍基于 spring 配置实现 Quartz 任务调度。
2.1、继承 QuartzJobBean 实现定时任务
定义任务处理类(org.springframework.scheduling.quartz.QuartzJobBean)
package org.fuys.own.test;import java.util.Date;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.scheduling.quartz.QuartzJobBean;/** * test QuartzJobBean class to execute time task * @author ys */public class QuartzJob extends QuartzJobBean{ private Logger logger = LoggerFactory.getLogger(QuartzJob.class); @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { logger.info(new Date().toString()); }}
复制代码
配置任务调度类(org.springframework.scheduling.quartz.JobDetailFactoryBean),即任务的调度由工厂类实现
<bean id="jobFactory" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="org.fuys.own.test.QuartzJob"/> <property name="jobDataMap"> <map> <entry key="timeout" value="0"/> </map> </property> </bean>
复制代码
配置任务调度的触发类(org.springframework.scheduling.quartz.SimpleTriggerFactoryBean),并配置触发时间
<bean id="jobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="jobFactory"/> <property name="startDelay" value="0"/> <property name="repeatInterval" value="2000"/> </bean>
复制代码
配置日程调度类(org.springframework.scheduling.quartz.SchedulerFactoryBean),即启动任务调度的触发
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <array> <ref bean="jobTrigger"/> </array> </property> </bean>
复制代码
配置好以上信息,定时程序交由 spring 容器自动处理。
2.2、使用 Cron 实现定时任务
Quartz 最大的好处在于使用 Cron 表达式进行定时任务的触发。
Cron 表达式的语法格式有两种:
具体的规则可以查看网络资料,无需记忆,具体问题时再查看即可。
对于程序而言,只需要将定时任务的触发类修改为支持 Cron 表达式的类即可,以下展示 Quartz 和 Cron 一起实现触发的比较。
<bean id="jobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="jobFactory"/> <property name="startDelay" value="1"/> <property name="repeatInterval" value="7000"/> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="jobFactory"/> <property name="cronExpression" value="0/30 * * * * ?"/> </bean> <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <array> <ref bean="jobTrigger"/> <ref bean="cronTrigger"/> </array> </property> </bean>
复制代码
2.3、无需继承父类的定时任务实现
无需继承父类的定时任务实现,只需要变更任务调度类,将其修改为以下类:
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
复制代码
配置信息如下:
<bean id="methodInvokingJobFactory" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <bean class="org.fuys.own.test.TimerDemo"/> </property> <property name="targetMethod" value="executeTimer"/> </bean>
复制代码
三、Spring Task
基于第三方组件 Quartz 的定时任务实现,配置过于复杂,不利于简便开发,因此,spring 提供了 task 组件,为开发者快速简便的进行定时任务实现提供了工具。
Task 组件的实现有两种方式:
配置文件添加 task 规则:
xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/taskhttp://www.springframework.org/schema/task/spring-task.xsd"
复制代码
3.1、基于配置文件的实现
Java 类:
package org.fuys.own.test;import java.util.Date;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class SpringTaskDemo { private Logger logger = LoggerFactory.getLogger(SpringTaskDemo.class); public void executeTask(){ logger.info("===>" + new Date().toString() + "<==="); }}
复制代码
applicationContext.xml 文件配置:
<bean id="springTask" class="org.fuys.own.test.SpringTaskDemo"/> <task:scheduled-tasks> <!-- 间隔执行 --> <task:scheduled ref="springTask" method="executeTask" fixed-delay="3000"/> <!-- Cron表达式 --> <task:scheduled ref="springTask" method="executeTask" cron="0/8 * * * * ?"/> </task:scheduled-tasks>
复制代码
3.2、基于 Annotation 的实现
因为有了 Annotation 这一特点,spring 容器的支持,所以,不需要配置那么多复杂的继承结构。使用 Spring Task 的注解配置,需要在配置文件中添加 task 的注解驱动支持。
配置文件:
<task:annotation-driven/>
复制代码
Java 程序:
package org.fuys.own.test;import java.util.Date;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;@Componentpublic class SpringTaskAnnotationDemo { private Logger logger = LoggerFactory.getLogger(SpringTaskAnnotationDemo.class); // 定时调度 @Scheduled(cron="0/6 * * * * ?") public void executeSpringTask(){ logger.info("$$$ " + new Date().toString() + " $$$"); } // 间隔调度 @Scheduled(fixedRate=4000) public void executeTaskAnnotation(){ logger.info(">>> " + new Date().toString() + " <<<"); }}
复制代码
3.3、任务调度池
下面看下程序代码,分析其情况。
Java 代码:
package org.fuys.own.test;import java.util.Date;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;@Componentpublic class SpringTaskAnnotationDemo { private Logger logger = LoggerFactory.getLogger(SpringTaskAnnotationDemo.class); // 定时调度 @Scheduled(cron="0/6 * * * * ?") public void executeSpringTask(){ logger.info("$$$ " + new Date().toString() + " $$$"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } // 间隔调度 @Scheduled(fixedRate=4000) public void executeTaskAnnotation(){ logger.info(">>> " + new Date().toString() + " <<<"); }}
复制代码
程序执行后的结果为:
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:48:58 CST 2018 <<<INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:00 CST 2018 $$$INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:12 CST 2018 $$$INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:24 CST 2018 $$$
复制代码
由此得知,对于 Spring task 组件而言,定时任务是顺序调度执行,当上一个程序没有执行完时,剩下的程序只能进行挂起等待。因此,这对于实际应用而言,是非常不利的,故 Spring 提出了任务调度池的解决方案,可以并行执行多个程序。
添加以下配置即可实现任务调度池。
<task:scheduler id="schedulerPool" pool-size="10"/>
复制代码
将以上 Java 程序执行之后的结果为:
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:37 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:41 CST 2018 <<<INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:58:42 CST 2018 $$$INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:45 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:49 CST 2018 <<<INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:53 CST 2018 <<<INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:58:54 CST 2018 $$$
复制代码
评论