当前位置:   article > 正文

spring定时任务详解spring schedule和spring-quartz

spring schedule

从实现的技术上来分类,java定时任务目前主要有三种:

  1. Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行;而且作业类需要集成java.util.TimerTask,一般用的较少。
  2. Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行;使用起来需要继承org.springframework.scheduling.quartz.QuartzJobBean,配置稍显复杂,所以,一般会使用spring集成quartz,稍后会详细介绍;
  3. Spring3.0以后自带的task,即:spring schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

综上,spring中使用定时任务有两种方式:spring schedule和spring-quartz,接下来我们重点介绍这两种。使用前都需要引入spring的包。

  1. <!-- spring -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-core</artifactId>
  5. <version>${org.springframework.version}</version>
  6. <type>jar</type>
  7. <scope>compile</scope>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework</groupId>
  11. <artifactId>spring-beans</artifactId>
  12. <version>${org.springframework.version}</version>
  13. <type>jar</type>
  14. <scope>compile</scope>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework</groupId>
  18. <artifactId>spring-context</artifactId>
  19. <version>${org.springframework.version}</version>
  20. <type>jar</type>
  21. <scope>compile</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework</groupId>
  25. <artifactId>spring-context-support</artifactId>
  26. <version>${org.springframework.version}</version>
  27. </dependency>

spring schedule

1、xml配置的方式:

1)application.xml:

首先在application.xml中引入task的命名空间,以及通过task标签定义任务。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:task="http://www.springframework.org/schema/task"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
  7. <task:scheduler id="myScheduler"/>
  8. <task:scheduled-tasks scheduler="myScheduler">
  9. <task:scheduled ref="doSomethingTask" method="doSomething" cron="0 * * * * *"/>
  10. </task:scheduled-tasks>
  11. </beans>

2)任务类:

  1. import org.springframework.scheduling.annotation.Scheduled;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class DoSomethingTask {
  5. public void doSomething() {
  6. System.out.println("do something");
  7. }
  8. }

2、@schedule 注解方式:

1)application.xml

同样在application.xml中引入task的命名空间,以及启用注解驱动的定时任务。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:task="http://www.springframework.org/schema/task"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
  7. <task:annotation-driven/>
  8. </beans>

2)任务类:

  1. import org.springframework.scheduling.annotation.Scheduled;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class DoSomethingTask {
  5. @Scheduled(cron="0 * * * * *")
  6. public void doSomething() {
  7. System.out.println("do something");
  8. }
  9. }

3、Cron表达式

由6~7项组成,中间用空格分开。从左到右依次是:秒、分、时、日、月、周几、年(可省略)。值可以是数字,也可以是以下符号:
*:所有值都匹配
?:无所谓,不关心,通常放在“周几”里
,:或者
/:增量值
-:区间

例如:

  1. 0 * * * * *:每分钟(当秒为0的时候)
  2. 0 0 * * * *:每小时(当秒和分都为0的时候)
  3. */10 * * * * *:每10
  4. 0 5/15 * * * *:每小时的5分、20分、35分、50
  5. 0 0 9,13 * * *:每天的9点和13

4、@Scheduled注解的另外两个属性:fixedRate和fixedDelay

 1)fixedDelay设置的是:上一个任务结束后多久执行下一个任务;

 2)fixedRate设置的是:上一个任务的开始到下一个任务开始时间的间隔;

注:如果是强调任务间隔的定时任务,建议使用fixedRate和fixedDelay,如果是强调任务在某时某分某刻执行的定时任务,建议使用cron表达式。

5、并发执行

1)spring schedule的定时任务默认是单线程的

处理方式是等待上一个任务执行完成后,再去执行下一个任务。(无论是cron还是fixedDelay、fixedRate都遵循这个原则)。下面举一些例子:

示例1:通过cron定时执行:任务运行时间6s、每5s运行一次任务

  1. //使用注解方式创建定时任务
  2. <task:annotation-driven/>
  3. @Component
  4. public class testTask {
  5. private Logger logger = LoggerFactory.getLogger(testTask.class);
  6. @Scheduled(cron = "0/5 * * * * ?")
  7. public void doTask() {
  8. logger.info(Thread.currentThread().getName()+"===task run");
  9. Thread.sleep(6*1_000);
  10. logger.info(Thread.currentThread().getName()+"===task end");
  11. }
  12. }
  13. //日志如下
  14. 2018-06-11 16:03:00.006 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  15. 2018-06-11 16:03:06.013 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  16. 2018-06-11 16:03:10.115 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  17. 2018-06-11 16:03:17.267 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  18. 2018-06-11 16:03:20.055 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  19. 2018-06-11 16:03:26.164 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end

根据日志可以看出,spring schedule默认是单线程处理的,下一个任务会等上一个运行完再执行。

示例2:换成fixedDelay

  1. @Scheduled(fixedDelay = 5*1_000)
  2. public void doTask() throws InterruptedException {
  3. logger.info(Thread.currentThread().getName()+"===task run");
  4. Thread.sleep(6*1_000);
  5. logger.info(Thread.currentThread().getName()+"===task end");
  6. }
  7. //日志如下:
  8. 2018-06-11 16:31:08.122 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  9. 2018-06-11 16:31:14.139 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  10. 2018-06-11 16:31:19.149 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  11. 2018-06-11 16:31:25.261 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  12. 2018-06-11 16:31:30.269 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  13. 2018-06-11 16:31:36.385 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end

从日志可以看出,任务结束时间再经过5s开始再次运行。

示例3:fixedRate

  1. @Scheduled(fixedRate = 5*1_000)
  2. public void doTask() throws InterruptedException {
  3. logger.info(Thread.currentThread().getName()+"===task run");
  4. Thread.sleep(6*1_000);
  5. logger.info(Thread.currentThread().getName()+"===task end");
  6. }
  7. //日志
  8. 2018-06-11 16:54:36.118 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  9. 2018-06-11 16:54:42.580 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  10. 2018-06-11 16:54:42.607 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  11. 2018-06-11 16:54:48.632 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  12. 2018-06-11 16:54:48.639 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  13. 2018-06-11 16:54:55.188 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end

从日志可以看出,上一个任务结束后,下一个任务立刻开始执行了,因为:fixedRate设置的上一个任务的开始时间到下一个任务开始时间的间隔。

示例4:fixedRate

上面例子,运行时间从6s改成2s,日志如下:

  1. 2018-06-11 17:08:43.086 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  2. 2018-06-11 17:08:45.093 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  3. 2018-06-11 17:08:48.025 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  4. 2018-06-11 17:08:50.083 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end
  5. 2018-06-11 17:08:53.239 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task run
  6. 2018-06-11 17:08:55.245 [pool-12-thread-1] INFO service.task.testTask -pool-12-thread-1===task end

结果和我们推断的一致,两个任务的开始时间间隔是5s。

2)配置并行处理

上面的例子是同一个task,如果前一个还没跑完后面一个就不会触发,这没有太大问题。但是,假设系统中有多个task,默认spring schedule是单线程的,就会造成不同的task也不能同时运行,就不太合理了。解决方法:

方法一:配置多个线程,但这样会导致同一个task前一个还没跑完后面又被触发的问题。

<task:scheduler id="scheduler" pool-size="2" />

方法二:让任务分别运行在不同的scheduler里

  1. <task:scheduler id="myScheduler1"/>
  2. <task:scheduler id="myScheduler2"/>
  3. <task:scheduled-tasks scheduler="myScheduler1">
  4. <task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/>
  5. </task:scheduled-tasks>
  6. <task:scheduled-tasks scheduler="myScheduler2">
  7. <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/>
  8. </task:scheduled-tasks>

spring-quartz

maven中需要引入:quartz.jar、spring-context-support.jar。

示例:

1)定义任务类:(spring集成的quartz不需要集成任何类)

  1. @Service
  2. public class QuartzTest {
  3. public void test(){
  4. System.out.println("It's time to run :" + new Date().toString());
  5. //TODO 执行任务逻辑
  6. //........
  7. }
  8. }

2)spring-quartz.xml配置:

a)SimpleTrigger方式:

  1. <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  2. <property name="triggers">
  3. <list>
  4. <ref local="quartzTestTrigger" />
  5. </list>
  6. </property>
  7. </bean>
  8. <bean id="quartzTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  9. <property name="jobDetail" ref="quartzTestJob"/>
  10. <!-- 20秒后运行 -->
  11. <property name="startDelay" value="20000" />
  12. <!-- 每隔三十秒重复 -->
  13. <property name="repeatInterval" value="30000" />
  14. </bean>
  15. <bean id="quartzTestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  16. <property name="targetObject" ref="quartzTest"></property>
  17. <property name="targetMethod" value="autoRun"></property>
  18. <property name="concurrent" value="false"></property><!--不并发运行-->
  19. </bean>

b)CronTrigger方式:

  1. <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  2. <property name="triggers">
  3. <list>
  4. <ref local="quartzTestTrigger" />
  5. </list>
  6. </property>
  7. <property name="startupDelay" value="500"/>
  8. </bean>
  9. <bean id="quartzTestTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
  10. <property name="jobDetail" ref="quartzTestJob"/>
  11. <property name="cronExpression">
  12. <value>0 */1 * * * ?</value>
  13. </property>
  14. </bean>
  15. <bean id="quartzTestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  16. <property name="targetObject" ref="quartzTest"></property>
  17. <property name="targetMethod" value="autoRun"></property>
  18. <property name="concurrent" value="false"></property><!--不并发运行-->
  19. </bean>

最后,在spring主配置文件application.xml中把上面的application-quartz.xml 包含进去即可。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号