【开发篇】十五、Spring Task实现定时任务

文章目录

  • 1、使用示例
  • 2、相关配置
  • 3、@Scheduled注解
  • 4、Spring Task单线程下的阻塞坑
  • 5、Spring Task阻塞问题的处理思路
  • 6、Spring Task在分布式环境中

上一篇用Quartz来实现了定时任务,但相对来说,这个框架还是比较繁琐。Spring Boot默认在无任何第三方依赖的情况下使用Spring-context模块下提供的定时任务工具 Spring Task

1、使用示例

@EnableScheduling开启定时任务功能:

@SpringBootApplication@EnableSchedulingpublic class TaskApplication {   public static void main(String[] args) {  SpringApplication.run(TaskApplication.class, args); }
}

接下来就可以通过@Scheduled注解方式自定义定时任务:

@Component
public class ScheduledBean {    @Scheduled(cron = "0/5 * * * * ?")    public void printLog(){  System.out.println(Thread.currentThread().getName()+":run...");  }
}

重启服务:

在这里插入图片描述

2、相关配置

以上是基本使用,还可配置一些通用配置,如下:

spring:  task:    scheduling:      # 任务调度线程池大小,因为要开线程异步,最多能开几个线程?,默认 1     pool:       size: 1      # 调度线程名称前缀 默认 scheduling-,程序线程一多,不好找,加了它可读性高     thread-name-prefix: ssm_     shutdown:        # 线程池关闭时,是否等待所有任务完成        await-termination: false        # 调度线程关闭前最大等待时间,确保最后一定关闭(如果等待,最多等几秒)        await-termination-period: 10s

注意thread-name-prefix配置,是日志里定时任务的线程的前缀,默认为scheduling-num,num即序号。以上这个配置由TaskSchedulingProperties类从yaml中接收。

在这里插入图片描述

3、@Scheduled注解

整理下@Scheduled注解的常用属性:

  • cron,cron表达式,语法跳这篇【cron表达式语法】
  • fixedDelay,固定时延。从上次任务执行结束的时间开始,到下个任务开始的时间间隔。不关心任务逻辑、任务本身执行多长时间。下图为fixedDelay为4s时的示意图:

在这里插入图片描述

  • fixedRate,固定频率。在理想情况下,下一次开始和上一次开始之间的时间间隔是一定的,但当如果上一次任务因为其他原因超时好久,而pool.size的默认值为1,即默认情况下 Spring Boot 定时任务是单线程执行的,那下一轮任务就会被阻塞。类比地铁每隔10分钟发一列,也就是说所有列车其实已经安排好了时刻表,理想情况下,每列车准点发就行了,互不影响,但是如果其中一列晚点,那么就会导致下一列晚点。

图片来源cloud.tencent.com/developer/article/1582434

  • initialDelay:初始化延迟时间,也就是第一次延迟执行的时间。这个参数对 cron 属性无效,只能配合 fixedDelay 或 fixedRate 使用。如 @Scheduled(initialDelay=5000,fixedDelay = 1000) 表示第一次延迟 5000 毫秒执行,下一次任务在上一次任务结束后 1000 毫秒后执行。

fixedDelay和fixedRate,都是和两轮任务有关,但前者关注的是第一轮的结束时间和第二轮的开始时间的这个间隔。而后者关注的都是两轮的开始时间中间的这个间隔。

4、Spring Task单线程下的阻塞坑

demo代码,演示两个任务在单线程下的阻塞:

@Component
public class ScheduleTask {/*** 上一次任务执行完后,歇一秒,再执行下一轮* 执行一次任务耗时5秒*/@Scheduled(fixedDelay = 1000)public void task1() throws InterruptedException {System.out.println(Thread.currentThread().getName()+ "==>  spring task 1 ==> "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS")));Thread.sleep(5000);}/*** 下轮任务在上一轮任务开始后2秒执行.* 执行一次任务耗时可忽略*/@Scheduled(fixedRate = 2000)public void task2() {System.out.println(Thread.currentThread().getName()+ "==>  spring task2 ==> "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS")));}}

执行效果:

在这里插入图片描述

可以看到task2被连续执行三次,且不妙的是两次任务开始时间没有间隔2s。这就是单线程下阻塞导致的问题,task1执行的5秒内,task2按预定的间隔触发的任务被阻塞,等task1一执行完,就会立刻执行这些阻塞的任务。这个延迟和堆积在生产中还是很严重的。

5、Spring Task阻塞问题的处理思路

第一种是直接改配置文件:

既然问题在单线程,一个线程处理不过来而导致的问题,那让定时任务的执行改为多线程就行了:

pring:  task:    scheduling:      # 任务调度线程池大小,因为要开线程异步,最多能开几个线程?,默认 1     pool:       size: 1     # 像上面的demo,设size为2即可 

第二种是定义配置类,实现SchedulingConfigurer接口,设置taskScheduler:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {//设定一个长度10的定时任务线程池,这个大小自己判断taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));}
}
以下是这种的方式之所以能实现的源码解析:

可以从@EnableScheduling注解源码开始分析得出,先看这个注解导入的SchedulingConfiguration类:

在这里插入图片描述

返回一个调度注解Bean的后置处理器:

在这里插入图片描述

往下跟这个后置处理器的构造方法,看到了ScheduledTaskRegistrar,

在这里插入图片描述

往下跟也就看到了为什么Spring Task默认是单线程的,这里new的是一个单线程的调度执行器:

在这里插入图片描述

而上面自己写配置类,setScheduler即可跳过上面的默认单线程配置。

第三种是加@Async注解开启异步任务

启动类加@EnableAsyn开启注解支持c,在定时任务方法上加入注解@Async

@Async
@Schedule(...)
public void task1(){//...
}@Async
@Schedule(...)
public void task2(){//...
}

如果有@Async这个注解的额外配置需求,参考:

//非必须,看自己需求
@Bean
public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();poolTaskExecutor.setCorePoolSize(4);poolTaskExecutor.setMaxPoolSize(6);// 设置线程活跃时间(秒)poolTaskExecutor.setKeepAliveSeconds(120);// 设置队列容量poolTaskExecutor.setQueueCapacity(40);poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 等待所有任务结束后再关闭线程池poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);return poolTaskExecutor;
}

6、Spring Task在分布式环境中

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/125606.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案_已设置图床

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案 minikube dashboard报错 considerconsider-Dell-G15-5511:~$ minikube dashboard 🤔 正在验证 dashboard 运行情况 ... 🚀 正在启动代理... 🤔 正在验证 proxy 运行…

2023/9/27 -- ARM

【汇编语言相关语法】 1.汇编语言的组成部分 1.伪操作:不参与程序的执行,但是用于告诉编译器程序该怎么编译 .text .global .end .if .else .endif .data2.汇编指令 编译器将一条汇编指令编译成一条机器码,在内存里一条指令占4字节内…

【C++设计模式之原型模式:创建型】分析及示例

简介 原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来生成新的对象,而无需再次使用构造函数。 描述 原型模式通过复制现有对象来创建新的对象,而无需显式地调用构造函数或暴露对象的创建…

013-第二代上位机开发环境搭建

第二代上位机开发环境搭建 文章目录 第二代上位机开发环境搭建项目介绍虚拟机安装Debian 10文件传输远程调试VNCrsync下载安装验证 配置远程调试环境配置远程设备配置 kitsCompilers配置Qtversions配置kits 测试 总结一下 关键字: Qt、 Qml、 关键字3、 关键字4…

Python常用功能的标准代码

后台运行并保存log 1 2 3 4 5 6 7 8 9 nohup python -u test.py > test.log 2>&1 & #最后的&表示后台运行 #2 输出错误信息到提示符窗口 #1 表示输出信息到提示符窗口, 1前面的&注意添加, 否则还会创建一个名为1的文件 #最后会把日志文件输出到test.log文…

八大排序算法汇总(C语言实现)

本专栏内容为:八大排序汇总 通过本专栏的深入学习,你可以了解并掌握八大排序以及相关的排序算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:八大排序汇总 🚚代码仓库:小小unicorn的代码仓库…

智慧公厕:将科技融入日常生活的创新之举

智慧公厕是当今社会中一项备受关注的创新项目。通过将科技融入公厕设计和管理中,这些公厕不仅能够提供更便利、更卫生的使用体验,还能够极大地提升城市形象和居民生活质量。本文将以智慧公厕领先厂家广州中期科技有限公司,大量的精品案例项目…

React项目部署 - Nginx配置

写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成…

Ubuntu无法引导启动的修复

TLDR:使用Boot-Repair工具。 Boot-Repair Boot-Repair是一个简单的工具,用于修复您在Ubuntu中可能遇到的常见启动问题,例如在安装Windows或其他Linux发行版后无法启动Ubuntu时,或者在安装Ubuntu后无法启动Windows时,…

Postman使用实例

Postman使用实例 实体类Emp package com.example.springboot_postman.pojo;import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import javax.persistence.*; import j…

C++设计模式-原型(Prototype)

目录 C设计模式-原型(Prototype) 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-原型(Prototype) 一、意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 二、适用性 当…

012-第二代硬件选型

第二代硬件选型 文章目录 第二代硬件选型项目介绍重新换平台缘由X86 && Arm 架构切换 ARM Linux 硬件选型系统确定Qt 版本确定总结一下 关键字: Qt、 Qml、 Arm、 X86、 linux 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QM…