SpringBoot定时任务详解
记录
使用springboot创建定时任务非常简单,目前主要有以下三种方式创建:
一、基于注解(@Scheduled)
二、基于接口(SchedulingConfigurer)
三、基于注解设定多线程定时任务
一、静态:基于注解
基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响,通过在配置类注解@EnableScheduling来开启对定时任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是需执行的定时任务
-
创建定时器
使用SpringBoot基于注解来创建定时任务非常简单,只需几行代码就能完成
@Configuration//主要用于标记配置类,兼备Component的效果 @EnableScheduling//开启定时任务 public class StaticScheduleTask{ //添加定时任务 @Scheduled(cron="0/5 * * * * ?") //或直接指定时间间隔,例如:5秒 //@Scheduled(fixedRate=5000) private void configureTasks(){ System.err.println("执行静态定时任务时间:"+LocalDateTime.now()); } }
Cron表达式参数分别表示:
秒(0~59)例如0/5表示每5秒
分(0~59)
时(0~23)
日(0~31)的某天,需计算
月(0~11)
周(可填1-7或SUN/MON/TUE/WED/THU/FRI/SAT)
@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如fixedDelay,fixedRate填写相应的毫秒数即可
cron表达式:依据业务需求,可设定具体的任务执行时间,预定时间一到就会自动执行; cron一共有7位,但是最后一位是年,可以留空,一般情况可以写6位: * 第一位,表示秒,取值0-59 * 第二位,表示分,取值0-59 * 第三位,表示小时,取值0-23 * 第四位,日期天/日,取值1-31 * 第五位,日期月份,取值1-12 * 第六位,星期,取值1-7,星期一,星期二...,注:不是第1周,第二周的意思,另外:1表示星期天,2表示星期一。 * 第7为,年份,可以留空,取值1970-2099 cron中,还有一些特殊的符号,含义如下: (*)星号:可以理解为每的意思,每秒,每分,每天,每月,每年... (?)问号:问号只能出现在日期和星期这两个位置。 (-)减号:表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12 (,)逗号:表达一个列表值,如在星期字段中使用“1,2,4”,则表示星期一,星期二,星期四 (/)斜杠:如:x/y,x是开始值,y是步长,比如在第一位(秒) 0/15就是,从0秒开始,每15秒,最后就是0,15,30,45,60 另:*/y,等同于0/y Cron表达式范例 每搁5秒执行一次:*/5 * * * * ? 每搁一分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月一号凌晨1点执行一次:0 0 1 1 * ? 每月最后一天23点执行一次:0 0 23 L * ? 每周星期天凌晨1点执行一次:0 0 1 ? * L 在26分、29分、33分执行一次:0 26,29,33 * * * ? 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
缺点:多个定时任务使用同一个调度线程,所以任务是阻塞执行,执行效率低;所有的任务都是在同一线程池中的同一个线程来完成
优点:使用与单个后台线程执行周期任务,并且保证按照预定时间执行
但是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便,为了达到实时生效的效果,可以使用接口来完成定时任务
二、动态:基于接口
实现SchedulingConfigure接口,重写configureTasks方法,添加需执行的定时任务;
-
导入依赖包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.4.RELEASE</version> </parent> <dependencies> <dependency><!--添加Web依赖 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><!--添加MySql依赖 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency><!--添加Mybatis依赖 配置mybatis的一些初始化的东西--> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <dependency><!-- 添加mybatis依赖 --> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> <scope>compile</scope> </dependency> </dependencies>
-
添加数据库数据(随便打开查询窗口,执行操作脚本内容),然后在项目中的application.yml添加数据源
spring datasource: url:jdbc:mysql://localhost:3306/数据库名称 username:root password:root
-
创建定时器
数据库准备好数据之后,我们编写定时任务,这里添加的是TriggerTask,目的是循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容
@Configuration//主要用于标记配置类,兼备component的效果 @EnableScheduling//开启定时任务 public class DynamicScheduleTask implements SchedulingConfigurer{ @Mapper public interface CronMapper{ @Select("select cron from cron limit 1") public String getCron(); } @Autowired//注入mapper @SuppressWarnings("all") CronMapper cronMapper; //执行定时任务 @Override public void configureTasks(ScheduledTask taskRegistrar){ taskRegistar.addTriggerTask{ //添加任务内容(Runnable) //其中configureTasks()是需要执行的任务 ()->System.out.println("执行动态定时任务:"+LocalDateTime.now().toLocalTime()), //设置执行周期(Trigger) triggerContext->{ //从数据库获取执行周期 String cron=cronMapper.getCron(); //合法性校验 //cron=0/5 * * * * ? if(StringUtils.isEmpty(cron)){ } //返回执行周期(Date) return new CronTrigger(cron).nextExecutiontime(triggerContext); } } } }//启动了springboot之后就会发现每次执行的时间差是数据库中设定的定时任务
注意:如果在数据库修改时格式出现错误,则定时任务会停止,即使重新修改正确,此时只能重新启动项目才能恢复
三、多线程定时任务
添加@EnableAsync开启对异步的支持
添加@Async注解,表示该定时任务是异步执行的,如果没有配置线程池的话会默认使用SimpleAsyncTaskExecutor,这个异步执行器每次都会开启一个子线程执行,性能消耗比较大
//@Component注解用于对那些比较中立的类进行注释 //相对于在持久层、业务层和控制层分别采用@Repository,@Service和@Controller对分层中的类进行注释 @Component @EnableScheduling//开启定时任务 @EnableAsync//开启多线程 public class MultithreadScheduleTask { @Async @Scheduled(fixedDelay = 1000)//间隔1秒 public void first() throws InterruptedException{ System.out.println("第一个定时任务开始:"+ LocalDateTime.now().toLocalTime()+"\r\n线程:"+Thread.currentThread()); System.out.println(); Thread.sleep(1000*10); } @Async @Scheduled(fixedDelay = 2000) public void second(){ System.out.println("第二个定时任务开始:"+LocalDateTime.now().toLocalTime()+"\r\n线程:"+Thread.currentThread()); } }
参考链接:
https://www.cnblogs.com/mmzs/p/10161936.html
https://blog.csdn.net/qq_41144667/article/details/103795004
https://blog.csdn.net/qq_41144667/article/details/103937193?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_v2~rank_aggregation-1-103937193.pc_agg_rank_aggregation&utm_term=springboot%E5%9F%BA%E4%BA%8E%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1&spm=1000.2123.3001.4430