spring boot 几种定时任务的实现方式
定时任务实现的几种方式:
创建任务类:
使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。
在传统的Spring项目中,我们可以在xml配置文件添加task的配置,而在SpringBoot项目中一般使用config配置类的方式添加配置,所以新建一个AsyncConfig类
@Configuration:表明该类是一个配置类
@EnableAsync:开启异步事件的支持
然后在定时任务的类或者方法上添加@Async 。最后重启项目,每一个任务都是在不同的线程中
执行时间的配置
在上面的定时任务中,我们在方法上使用@Scheduled注解来设置任务的执行时间,并且使用三种属性配置方式:
fixedRate:定义一个按一定频率执行的定时任务
fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,改属性可以配合initialDelay, 定义该任务延迟执行时间。
cron:通过表达式来配置任务执行时间
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。按顺序依次为:
其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于”月份中的日期”和”星期中的日期”这两个元素互斥的,必须要对其中一个设置。配置实例:
有些子表达式能包含一些范围或列表
“*”字符代表所有可能的值
“/”字符用来指定数值的增量
“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值
当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”
“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写
如果在“L”前有具体的内容,它就具有其他的含义了。
W 字符代表着平日(Mon-Fri),并且仅能用于日域中。它用来指定离指定日的最近的一个平日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。
C:代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。
在线cron表达式生成:
如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用 spring-boot-starter-quartz 依赖:
如果是1.5.9则要使用以下添加依赖:
这里我使用SpringBoot版本是 2.0.0.BUILD-SNAPSHOT ,该版本开始集成了Quartz,所以事实现起来很方便。其它好像比较麻烦,这里就不介绍,以后有时间再详细深入了解Quartz。
上面都是简单的介绍了关于SpringBoot定时任务的处理,直接使用SpringTask注解的方式应该是最方便的,而使用Quartz从2.0开始也变得很方便。对于这两种方式,应该说各有长处吧,按需选择。另外关于Quartz的详细内容可以查看官方文档: 传送门
Spring task定时任务
在Java中有三种实现定时任务的方式:1.java自带的API java.util.Timer类 java.util.TimerTask类 。2. Quartz框架 开源 功能强大 使用起来稍显复杂. 3.Spring 3.0以后自带了 task 调度工具,比Quartz更加的简单方便.
Spring从3.0后自带了task调度工具,不需要引入其他的第三方依赖。在启动类上添加 @EnableScheduling 注解
ScheduleTask.java
在需要定时执行的方法上添加 @Scheduled 注解并指定cron的值,上面的这个例子让打印语句每天凌晨两点执行一次。
这个注解标记了一个将要被定时执行的方法, cron 、 fixedDelay 与 fixedRate 三个属性必选其一。
被注解的方法不能传入参数,通常有一个 void 的返回值,如果不是,返回值将会被忽略。
cron 是一个类似cron的表达式,可以指定秒、分、时、一个月的第几天、月、一周的星期几。例如,"0 * * * * MON-FRI"表示工作日的每一分钟都执行。
zone 指定了cron表达式的时区。如果未指定,则是服务器的默认时区。
fixedDelay :执行注解方法的固定的毫秒数间隔,这个间隔是指上一次调用的结束和下一次调用的开始的时间。
fixedRate :执行注解方法的固定的毫秒数间隔,这个间隔是指每次调用之间的时间。与上面的区别是:fixedDelay是前一个方法执行完毕后的固定时间再执行下一个方法,fixedRate是上一个方法开始执行固定时间后执行下一个方法。
cron表达式可以分为两种:
1、6位长度的秒 分 时 日 月 星期
2、7位长度的秒 分 时 日 月 星期 年
一般都是用6位长度的。
秒:可出现 , - * / 四个字符,有效范围为0-59的整数
分:可出现 ,- * / 四个字符,有效范围为0-59的整数
时:可出现 ,- * / 四个字符,有效范围为0-23的整数
日:可出现 ,- * / ? L W C 八个字符,有效范围为0-31的整数
月:可出现 ,- * / 四个字符,有效范围为1-12的整数或JAN-DEC
星期:可出现 ,- * / ? L C # 八个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天
年:可出现 ,- * / 四个字符,有效范围为1970-2099年
(1)*:表示匹配该域的任意值,假如在Minutes域使用*,即表示每分钟都会触发事件。
(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。
例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?,其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
(3)-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
(4)/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.
(5),:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
(6)L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。
例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;
如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份
(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
有时候需要执行的定时任务会很多,如果是串行执行会带来一些问题,比如一个很耗时的任务阻塞住了,一些需要短周期循环执行的任务也会卡住,所以可以配置一个线程池来并行执行定时任务。
有两种配置方式,一种是写一个配置类创建一个线程池,另一种是在yml文件中进行配置创建线程池。
配置文件的方式:
Spring调度定时任务的方式
spring 定时器任务scheduled-tasks默认配置是单线程串行执行的,多个任务相当于串行。每个job都是等待上个执行完了才执行下一个job。这就造成了若某个任务执行时间过长,其他任务一直在排队,业务逻辑没有及时处理的问题。
spring调度定时任务的方式就会导致:bTask会因为aTask的超时执行而延迟执行。
如下是scheduled定义了3个任务。
查看该任务17点的执行日志(task名字已修改)
MyTask2每10秒钟执行一次,但是在17:15:05 到17:20:35之间,5分钟内定时任务没有执行。
执行命令 zgrep -e '2016-10-28 17:1' task.log.2016-10-28.log.gz (当天17点10几分发生的日志)。然后在查询日志发现,这5分钟之内有大量的日志在执行。
通过过以上日志可以看出,该线程 [pool-8-thread-1 - ] 一直在处理MyTask3任务,此时断定 task:scheduled 配置默认是单线程串行的。网上查找资料发现如下配置可以解决问题:
Spring中可以通过配置方便的实现周期性定时任务管理,这需要用到以下几个类:
concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,第二个job就开始了。定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始。
MethodInvokingJobDetailFactoryBean类默认是并发执行的,这时候如果不设置“concurrent”为false,很可能带来并发或者死锁的问题,而且几率较小,不容易复现,请大家使用的时候注意设置“concurrent”。
Spring使用@Scheduled注解配置定时任务
项目中经常会用到定时任务。所以在这里总结一下在SSM框架中如何配置定时任务。
1、在spring的配置文件spring.xml(文件名可以任意)中增加如下配置
1):spring配置文件加入头部加入
2):spring配置文件加入定时任务注解配置
3):spring配置文件加入定时任务扫描包
4):spring配置文件加入配置定时任务的线程池。因为spring的定时任务默认是单线程,多个任务执行起来时间会有问题。
2、在package com.sc.api下新增定时任务相关类ScheduledApiTest
调用的两种方式:
@Scheduled注解为定时任务,@Component 把普通pojo实例化到spring容器中,相当于配置文件中的bean id="" class=""/
1):如果需要以固定速率执行,只要将注解中指定的属性名称改成fixedRate即可,以下方法将以一个固定速率1分钟来调用一次执行,这个周期是以上一个任务开始时间为基准,从上一任务开始执行后1分钟再次调用。
@Scheduled(fixedRate = 1000 60 30) //心跳更新。启动时执行一次,之后每隔1分钟执行一次
2):如果你需要在特定的时间执行,就需要用到cron,cron表达式里为执行的时机
@Scheduled(cron = "0 34 13 * * ?") //每天的13点30分执行一次。
3、启动tomcat服务,定时任务就会按时执行。
关于CRON表达式 含义