spring定时任务详解springschedule和spring-quartz
从实现的技术上来分类,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的包。
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
spring schedule
1、xml配置的⽅式:
1)l:
⾸先在l中引⼊task的命名空间,以及通过task标签定义任务。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:task="/schema/task"
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans-4.0.xsd /schema/task /schema/task/spring-task-4.0.xsd">
<task:scheduler id="myScheduler"/>
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="doSomethingTask" method="doSomething" cron="0 * * * * *"/>
</task:scheduled-tasks>
</beans>
2)任务类:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DoSomethingTask {
public void doSomething() {
System.out.println("do something");
}
}
2、@schedule 注解⽅式:
1)l
同样在l中引⼊task的命名空间,以及启⽤注解驱动的定时任务。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:task="/schema/task"
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans-4.0.xsd /schema/task /schema/task/spring-task-4.0.xsd">
<task:annotation-driven/>
</beans>
2)任务类:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DoSomethingTask {
@Scheduled(cron="0 * * * * *")
public void doSomething() {
System.out.println("do something");
}
}
3、Cron表达式:
由6~7项组成,中间⽤空格分开。从左到右依次是:秒、分、时、⽇、⽉、周⼏、年(可省略)。值可以是数字,也可以是以下符号:
*:所有值都匹配
:⽆所谓,不关⼼,通常放在“周⼏”⾥
,:或者
/:增量值
-:区间
例如:
0 * * * * *:每分钟(当秒为0的时候)
0 0 * * * *:每⼩时(当秒和分都为0的时候)
*/10 * * * * *:每10秒
0 5/15 * * * *:每⼩时的5分、20分、35分、50分
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运⾏⼀次任务
//使⽤注解⽅式创建定时任务
<task:annotation-driven/>
@Component
public class testTask {
private Logger logger = Logger(testTask.class);
@Scheduled(cron = "0/5 * * * * ?")
public void doTask() {
logger.info(Thread.currentThread().getName()+"===task run");
Thread.sleep(6*1_000);
logger.info(Thread.currentThread().getName()+"===task end");
}
}
//⽇志如下
2018-06-11 16:03:00.006 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:03:06.013 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:03:10.115 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:03:17.267 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:03:20.055 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:03:26.164 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
根据⽇志可以看出,spring schedule默认是单线程处理的,下⼀个任务会等上⼀个运⾏完再执⾏。
⽰例2:换成fixedDelay
@Scheduled(fixedDelay = 5*1_000)
public void doTask() throws InterruptedException {
thread技术logger.info(Thread.currentThread().getName()+"===task run");
Thread.sleep(6*1_000);
logger.info(Thread.currentThread().getName()+"===task end");
}
//⽇志如下:
2018-06-11 16:31:08.122 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:31:14.139 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:31:19.149 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:31:25.261 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:31:30.269 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:31:36.385 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
从⽇志可以看出,任务结束时间再经过5s开始再次运⾏。
⽰例3:fixedRate
@Scheduled(fixedRate = 5*1_000)
public void doTask() throws InterruptedException {
logger.info(Thread.currentThread().getName()+"===task run");
Thread.sleep(6*1_000);
logger.info(Thread.currentThread().getName()+"===task end");
}
//⽇志
2018-06-11 16:54:36.118 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:54:42.580 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:54:42.607 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:54:48.632 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 16:54:48.639 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 16:54:55.188 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
从⽇志可以看出,上⼀个任务结束后,下⼀个任务⽴刻开始执⾏了,因为:fixedRate设置的上⼀个任务的开始时间到下⼀个任务开始时间的间隔。
⽰例4:fixedRate
上⾯例⼦,运⾏时间从6s改成2s,⽇志如下:
2018-06-11 17:08:43.086 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 17:08:45.093 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 17:08:48.025 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 17:08:50.083 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
2018-06-11 17:08:53.239 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task run
2018-06-11 17:08:55.245 [pool-12-thread-1] INFO stTask -pool-12-thread-1===task end
结果和我们推断的⼀致,两个任务的开始时间间隔是5s。
2)配置并⾏处理
上⾯的例⼦是同⼀个task,如果前⼀个还没跑完后⾯⼀个就不会触发,这没有太⼤问题。但是,假设系统中有多个task,默认spring schedule是单线程的,就会造成不同的task也不能同时运⾏,就不太合理了。解决⽅法:
⽅法⼀:配置多个线程,但这样会导致同⼀个task前⼀个还没跑完后⾯⼜被触发的问题。
<task:scheduler id="scheduler" pool-size="2" />
⽅法⼆:让任务分别运⾏在不同的scheduler⾥
<task:scheduler id="myScheduler1"/>
<task:scheduler id="myScheduler2"/>
<task:scheduled-tasks scheduler="myScheduler1">
<task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/>
</task:scheduled-tasks>
<task:scheduled-tasks scheduler="myScheduler2">
<task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/>
</task:scheduled-tasks>
spring-quartz
maven中需要引⼊:quartz.jar、spring-context-support.jar。
⽰例:
1)定义任务类:(spring集成的quartz不需要集成任何类)
@Service
public class QuartzTest {
public void test(){
System.out.println("It's time to run :" + new Date().toString());
//TODO 执⾏任务逻辑
//........
}
}
2)l配置:
a)SimpleTrigger⽅式:
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="quartzTestTrigger" />
</list>
</property>
</bean>
<bean id="quartzTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="quartzTestJob"/>
<!-- 20秒后运⾏ -->
<property name="startDelay" value="20000" />
<!-- 每隔三⼗秒重复 -->
<property name="repeatInterval" value="30000" />
</bean>
<bean id="quartzTestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFac
toryBean"> <property name="targetObject" ref="quartzTest"></property>
<property name="targetMethod" value="autoRun"></property>
<property name="concurrent" value="false"></property><!--不并发运⾏-->
</bean>
b)CronTrigger⽅式:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论