玩转SpringBoot:SpringBoot的几种定时任务实现方式

引言
在现代软件开发中,定时任务是一种常见的需求,用于执行周期性的任务或在特定的时间点执行任务。这些任务可能涉及数据同步、数据备份、报表生成、缓存刷新等方面,对系统的稳定性和可靠性有着重要的影响。Spring Boot提供了强大且简单的定时任务功能,使开发人员能够轻松地管理和执行这些任务。

本文将介绍 Spring Boot中定时任务的基本用法、高级特性以及最佳实践,帮助开发人员更好地理解和应用定时任务,提高系统的稳定性和可靠性。

SpringBoot中的定时任务
SpringBoot中的定时任务主要通过@Scheduled注解以及SchedulingConfigurer接口实现。

@Scheduled注解
@Scheduled注解是Spring提供的一个注解,用于标记方法作为定时任务执行。通过 @Scheduled注解,开发人员可以轻松地配置方法在指定的时间间隔或时间点执行,实现各种定时任务需求。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

String cron() default "";long fixedDelay() default -1;long fixedRate() default -1;long initialDelay() default -1;

}
以上为@Scheduled源码中关键属性,各属性含义如下:

cron: 接受标准的Unix Cron表达式,用于定义复杂的计划执行时间。
/**

  • cron属性可以设置指定时间执行,cron表达式跟linux一样
    /
    @Scheduled(cron = "0 45 14 ? * ")
    public void fixTimeExecution() {
    System.out.println("指定时间 "+dateFormat.format(new Date())+“执行”);
    }
    fixedRate: 以固定的频率执行任务,指定两次执行之间的间隔时间(单位是毫秒)。
    /
  • fixedRate属性设置每隔固定时间执行
    /
    @Scheduled(fixedRate = 5000)
    public void reportCurrentTime() {
    System.out.println(“每隔五秒执行一次” + dateFormat.format(new Date()));
    }
    fixedDelay:在每次任务完成后等待一定的时间再进行下一次执行,指定连续执行之间的延迟时间。
    /
    *
  • 上一次任务执行完成之后10秒后在执行
    /
    @Scheduled(fixedDelay = 10000)
    public void runWithFixedDelay() {
    System.out.println("指定时间 "+dateFormat.format(new Date())+“执行”);
    }
    initialDelay:首次执行前的延迟时间。
    /
    *
  • 初始延迟1秒后开始,然后每10秒执行一次
    */
    @Scheduled(initialDelay=1000, fixedDelay=10000)
    public void executeWithInitialAndFixedDelay() {
    System.out.println("指定时间 "+dateFormat.format(new Date())+“执行”);
    }
    这里要注意fixedRate与fixedDelay的区别:fixedRate是基于任务开始执行的时间点来计算下一次任务开始执行的时间,因此任务的执行时间间隔是相对固定的,不受到任务执行时间的影响。如果指定的时间间隔小于任务执行的实际时间,则任务可能会并发执行。而fixedDelay是基于任务执行完成的时间点来计算下一次任务开始执行的时间,因此任务的执行时间间隔是相对不规则的,受到任务执行时间的影响。

SpringBoot支持同时定义多个定时任务方法,每个方法可以使用不同的参数配置,以满足不同的定时任务需求。同时,我们必须在配置类中使用@EnableScheduling注解开启定时任务。

@Configuration
@EnableScheduling
public class ScheduledTaskConfig {

}
或者

@EnableScheduling
@SpringBootApplication
public class SpringBootBaseApplication {

public static void main(String[] args) {  SpringApplication.run(SpringBootBaseApplication.class, args);  
}  

}
在SpringBoot应用程序中,除了在代码中使用注解配置定时任务外,还可以通过配置文件来配置定时任务的执行规则。这种方式更加灵活,可以在不修改源代码的情况下,动态调整定时任务的执行规则。比如我们在application.properties中配置@Scheduled的属性:

custom.scheduled.cron = 0/5 * * * * ?
custom.scheduled.fixedRate=5000
custom.scheduled.fixedDelay=10000
custom.scheduled.initialDelay=1000
然后在@Scheduled的方法使用属性配置定时任务执行频率。

@Service
public class DemoScheduledTaskService {

private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");  /**  * fixedRate属性设置每隔固定时间执行  */  
@Scheduled(fixedRateString = "${custom.scheduled.fixedRate}")  
public void reportCurrentTime() {  System.out.println("每隔五秒执行一次" + dateFormat.format(new Date()));  
}  /**  * cron属性可以设置指定时间执行,cron表达式跟linux一样  */  
@Scheduled(cron = "${custom.scheduled.cron}")  
public void fixTimeExecution() {  System.out.println("指定时间 "+dateFormat.format(new Date())+"执行");  
}  /**  * 上一次任务执行完成之后10秒后在执行  */  
@Scheduled(fixedDelayString = "${custom.scheduled.fixedDelay}")  
public void runWithFixedDelay() {  System.out.println("指定时间 "+dateFormat.format(new Date())+"执行");  
}  /**  * 初始延迟1秒后开始,然后每10秒执行一次  */  
@Scheduled(initialDelayString = "${custom.scheduled.initialDelay}", fixedDelayString = "${custom.scheduled.fixedDelay}")  
public void executeWithInitialAndFixedDelay() {  System.out.println("指定时间 "+dateFormat.format(new Date())+"执行");  
}  

}
注意,这里使用属性来指定任务执行频率时,要通过@Scheduled的fixedRateString、fixedDelayString、initialDelayString三个可以指定字符串的值的属性去指定,效果等同于long类型的属性。

通过配置文件配置定时任务具有很高的灵活性,可以在不重新编译和部署应用程序的情况下,随时调整定时任务的执行规则。同时,也可以根据不同的环境(例如开发、测试、生产)配置不同的定时任务规则,以满足不同环境下的需求。这种方式可以有效地解耦定时任务的配置和业务代码,提高系统的灵活性和可维护性。

另外,如果希望定时任务能够异步执行,不阻塞主线程,可以在方法上同时加上@Async注解,这样各任务就可以异步执行了。有关SpringBoot中使用@Async的讲解,请移步:

虽然 @Scheduled 注解是一个方便的方式来定义定时任务,但它也存在一些弊端。因为任务的执行计划(如cron表达式)在编译时被硬编码,因此无法在运行时动态修改,除非重新部署。此外,@Scheduled注解对于配置不同的调度策略(如使用不同的线程池)显得力不从心,而且默认情况下,@Scheduled任务在单线程环境下执行,可能出现任务堆积的情况,尤其在任务量大或任务执行时间长的情况下,而且这些任务可能会变得混乱和难以管理。定时任务的配置分散在各个任务方法中,不利于统一管理和维护。对于需要根据动态条件创建或销毁定时任务的情况,@Scheduled注解也无法满足需求。

为了解决这些问题,可以使用SchedulingConfigurer接口来动态地创建和管理定时任务。通过实现 SchedulingConfigurer 接口,我们可以编写代码来动态地注册和管理定时任务,从而实现灵活的任务调度需求。接下来,我们将介绍如何使用SchedulingConfigurer接口来创建定时任务。

SchedulingConfigurer接口
SchedulingConfigurer 接口是 Spring 提供的一个用于定时任务配置的扩展接口,它允许开发人员更细粒度地控制定时任务的执行。通过实现SchedulingConfigurer接口,可以自定义任务调度器(TaskScheduler),配置线程池等参数,以满足不同场景下的定时任务需求。

@Configuration
@EnableScheduling
public class CustomSchedulingConfig implements SchedulingConfigurer {

@Override  
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {// 定时任务逻辑
}

}
通过实现SchedulingConfigurer接口,重写configureTasks方法,自定义任务调度器的配置。此外我们还可以配置线程池,用于控制定时任务执行时的线程数量、并发性等参数。

@Bean(destroyMethod = “shutdown”)
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // 设置线程池大小
scheduler.setThreadNamePrefix(“scheduled-task-”); // 设置线程名称前缀
scheduler.setAwaitTerminationSeconds(60); // 设置终止等待时间
// 设置处理拒绝执行的任务异常
scheduler.setRejectedExecutionHandler((r, executor) -> log.error(“Task rejected”, r));
// 处理定时任务执行过程中抛出的未捕获异常
scheduler.setErrorHandler(e -> log.error(“Error in scheduled task”, e));
return scheduler;
}
然后将自定义的ThreadPoolTaskScheduler设置到ScheduledTaskRegistrar中去:

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 定时任务逻辑
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler());
}
有关线程池的配置参数讲解,请移步:

通过SchedulingConfigurer接口,可以更灵活地配置任务调度器和定时任务的执行规则,比如动态注册定时任务、动态修改任务执行规则等。

动态添加定时任务
在SchedulingConfigurer的configureTasks方法中,我们可以根据业务需求,从数据库、配置文件或其它动态来源获取定时任务的信息(如Cron表达式、任务执行类等),然后创建对应的Runnable或Callable实例,并结合Trigger(如CronTrigger)将其添加到调度器中。相比@Scheduled注解,这种方式能够在应用运行时随时添加新的定时任务。
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler scheduler = threadPoolTaskScheduler();
taskRegistrar.setTaskScheduler(scheduler);

List<CronTaskInfo> tasksFromDB = listTasksFromDatabase();  for (CronTaskInfo task : tasksFromDB) {  Runnable taskRunner = new MyTaskExecutor(task.getTaskData());  CronTrigger cronTrigger = new CronTrigger(task.getCronExpression());  scheduler.schedule(taskRunner, cronTrigger);  
}  

}
关于这里在应用运行时,动态的添加新的任务,我们可以通过事件驱动,轮训检查,消息队列等多种方式,监听到数据库中或者配置文件中新增任务信息,然后通过SchedulingConfigurer接口动态创建定时任务。而这种方式是@Scheduled注解做不到的。

修改定时任务规则
当任务的执行规则需要动态变更时,同样可以在configureTasks方法中实现。例如,从数据库获取最新的Cron表达式,然后取消当前任务并重新添加新的任务实例。需要注意的是,取消已有任务通常需要持有对该任务的引用,例如使用Scheduler提供的unschedule方法。
// 假设我们有一个方法用于获取更新后的任务信息
CronTaskInfo updatedTask = getUpdatedTaskInfoFromDatabase();

// 取消旧的任务(需要知道旧任务的TriggerKey)
TriggerKey triggerKey = …; // 获取旧任务的TriggerKey
scheduler.unschedule(triggerKey);

// 创建新任务并设置新的Cron表达式
MyTaskExecutor taskExecutor = new MyTaskExecutor(updatedTask.getTaskData());
CronTrigger updatedCronTrigger = new CronTrigger(updatedTask.getCronExpression());

// 重新调度新任务
scheduler.schedule(taskRunner, updatedCronTrigger);
另外,我们还可以通过添加任务时对其排序或设置优先级等方式间接实现设置定时任务的执行顺序。

通过实现SchedulingConfigurer接口,我们可以拥有对定时任务调度的更多控制权,比如自定义线程池、动态添加任务以及调整任务执行策略。这种灵活性使得在复杂环境下,特别是需要动态管理定时任务时,SchedulingConfigurer成为了理想的选择。

其他第三方任务调度框架
除了使用Spring框架提供的 @Scheduled 注解和SchedulingConfigurer接口外,还有许多第三方的任务调度库可供选择。这些库通常提供了更多的功能和灵活性,以满足各种复杂的任务调度需求。以下是一些常见的第三方任务调度库:

Quartz Scheduler:
Quartz是一个功能强大且灵活的任务调度库,具有丰富的功能,如支持基于cron表达式的任务调度、集群支持、作业持久化等。它可以与Spring框架集成,并且被广泛应用于各种类型的任务调度应用程序中。

Elastic Job:
Elastic Job是一个分布式任务调度框架,可以轻松实现分布式任务调度和作业执行。它提供了分布式任务执行、作业依赖关系、作业分片等功能,适用于大规模的分布式任务调度场景。

xxl-job:
xxl-job是一个分布式任务调度平台,提供了可视化的任务管理界面和多种任务调度方式,如单机任务、分布式任务、定时任务等。它支持任务执行日志、任务失败重试、动态调整任务执行策略等功能。

PowerJob:
PowerJob是一个开源的分布式任务调度框架,由阿里巴巴集团开发并开源。PowerJob 提供了分布式、高可用的任务调度能力,支持多种任务类型,如定时任务、延时任务、流程任务等。

总结
定时任务在现代软件开发中扮演着重要的角色,它们可以自动化执行各种重复性的任务,提高系统的效率和可靠性。SpringBoot提供了强大而灵活的定时任务功能,使我们能够轻松地管理和执行各种定时任务。通过@Scheduled注解和SchedulingConfigurer接口,我们可以根据需求配置定时任务的执行规则,实现各种复杂的定时任务调度需求。我们可以充分利用SpringBoot中的定时任务功能,提高系统的稳定性和可靠性,从而更好地满足业务需求。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等。

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

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

相关文章

VikeCTF 2024

VikeCTF 2024 WP 由于环境原因很多靶场的过程图片没法复现截图。。。 web Ponies 算是一个签到题&#xff0c;看到源码这里其实是一个快速跳转的程序&#xff0c;我们看到tag.src直接进行访问&#xff0c;可以看到源码里面&#xff0c;从里面分析拿到flag function recurs…

分享那些在云发生的奇妙故事 ~~ 征文挑战赛来啦!

云布道师 简介&#xff1a; 在数字化转型的大潮下&#xff0c;云计算已深度渗透各行各业&#xff0c;成为企业创新和发展的关键驱动力。本栏目旨在通过全面挖掘并生动展现“客户如何用好云”的实战历程&#xff0c;深入剖析行业痛点解决之道&#xff0c;以鲜活的案例故事呈现云…

Rust 语言的 for 循环用法

在 Rust 中&#xff0c;for 循环是一种用于迭代集合&#xff08;如数组、切片、向量、字符串、映射、通道等&#xff09;或其他可迭代对象的方式。Rust 的 for 循环语法相对直观&#xff0c;并且与许多其他编程语言中的 for 循环类似。 以下是 for 循环在 Rust 中的一些常见用…

流畅的Python(十八)-使用asyncio包处理并发

一、核心要义 1. 对比一个简答的多线程程序和对应的asyncio版,说明多线程和异步任务之间的关系 2. 网络下载的异步版 3. 在异步编程中,与回调相比&#xff0c;协程显著提升性能的方式 二、代码示例 1、相关知识点 #!/usr/bin/env python # -*- coding: utf-8 -*- # Time …

算法50:动态规划专练(力扣514题:自由之路-----4种写法)

题目: 力扣514 &#xff1a; 自由之路 . - 力扣&#xff08;LeetCode&#xff09; 题目的详细描述&#xff0c;直接打开力扣看就是了&#xff0c;下面说一下我对题目的理解: 事例1&#xff1a; 输入: ring "godding", key "gd" 输出: 4. 1. ring的第…

RStudio更换R语言版本

今天下载R语言用于读取.xlsx文件的readxl包时&#xff0c;RStudio提示该包是使用R-4.3.3版本构建&#xff0c;而我现在使用的是R-4.3.2版本&#xff0c;所以需要升级一下R语言版本&#xff0c;这里先下载最新版本的R语言&#xff0c; 下载地址&#xff1a;The Comprehensive R…

Jenkins自动构建 CI/CD流水线学习笔记(从入门到入土,理论+示例)

文章目录 1、什么是Jenkins的流水线?2、流水线语法2.1、声明式流水线2.2、脚本化流水线 3、流水线示例3.1、使用声明式流水线的语法编写的 Jenkinsfile 文件3.2、Pipeline 各种语言示例3.2.1 Java&#xff1a;3.2.2 Node.js / JavaScript3.2.3 Python 4、一套完整的Devops Jen…

【ICCV21】Swin Transformer: Hierarchical Vision Transformer using Shifted Windows

文章目录 0. Abstract1. Introduction2. Related Work3. Method3.1 Overall Architecture3.2 Shifted Window based Self-Attention3.3 Architecture Variants 4. Experiments4.1 Image Classification on ImageNet-1K4.2 Object Detection on COCO4.3 Semantic Segmentation o…

基于JavaWeb开发的springboot网咖管理系统[附源码]

基于JavaWeb开发的springboot网咖管理系统[附源码] &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统 &a…

【办公类-40-02】20240311 python模仿PPT相册功能批量插入照片,更改背景颜色 (家长会系列二)

作品展示——用Python插入PPT相册 背景需求&#xff1a; 马上就要家长会&#xff0c;我负责做会议前的照片滚动PPT&#xff0c;通常都是使用PPT的相册功能批量导入照片&#xff0c; 生成给一个新的PPT文件 更改背景颜色 设置4秒间隔&#xff0c;应用到全部 保存&#xff0c;改…

springboot单体项目链路日志跟踪及接口耗时

最近接触一个新的传统项目&#xff0c;在联调过程中&#xff0c;查看日志特别不方便&#xff0c;既无trackId&#xff0c;即无接口耗时&#xff0c;所以写了该博客。话不多说&#xff0c;直接上代码 1、实体类user package com.yk.domain;import lombok.Data;Data public cla…

【IVA】人工智能领域常用的术语(1)

在人工智能和机器学习领域&#xff0c;"检测"、"识别"和"分类"是常用的术语&#xff0c;它们在问题解决中有着不同的含义&#xff1a; 检测&#xff08;Detection&#xff09;&#xff1a;检测是指在图像或视频中定位和识别特定目标的过程。目标…

Hadoop伪分布式配置--没有DataNode或NameNode

一、原因分析 重复格式化NameNode 二、解决方法 1、输入格式化NameNode命令&#xff0c;找到data和name存放位置 ./bin/hdfs namenode -format 2、删除data或name&#xff08;没有哪个删哪个&#xff09; sudo rm -rf data 3、重新格式化NameNode 4、重新启动即可。

sheng的学习笔记- AI-类别不平衡问题

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 什么是类别不平衡问题 类别不平衡&#xff08;class-imbalance&#xff09;&#xff0c;也叫数据倾斜&#xff0c;数据不平衡&#xff0c;就是指分类任务中不同类别的训练样例数目差别很大的情况。 例如有998个反例&#xf…

vue3全局引入element-plus后怎么使用Message进行消息提示

全局引入 main.ts import element-plus/dist/index.css 在需要使用提示的组件中引入 import { ElMessage } from element-plus 使用举例

Verilog刷题笔记37

题目&#xff1a;3位二进制加法器 Now that you know how to build a full adder, make 3 instances of it to create a 3-bit binary ripple-carry adder. The adder adds two 3-bit numbers and a carry-in to produce a 3-bit sum and carry out. To encourage you to actua…

html5cssjs代码 001 第一个网页

html5&css&js代码 001 第一个网页 一、代码二、解释 这是第一个网页&#xff0c;也是一个模板。 一、代码 <!-- 声明文档类型 --> <!DOCTYPE html> <html lang "zh-cn" ><!-- 页面头部开始 --><head ><!-- 设置页面标题 …

@Conditional注解详解

目录 一、Conditional注解作用 二、Conditional源码解析 2.1 Conditional源码 2.2 Condition源码 三、Conditional案例 3.1 Conditional作用在类上案例 3.1.1 配置文件 3.1.2 Condition实现类 3.1.3 Bean内容类 3.1.4 Config类 3.1.5 Controller类 3.1.6 测试结果 3…

Visual grounding-视觉定位任务介绍

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

Spring Cloud Alibaba微服务从入门到进阶(一)

Springboot三板斧 1、加依赖 2、写注解 3、写配置 Spring Boot Actuator Spring Boot Actuator 是 Spring Boot 提供的一系列用于监控和管理应用程序的工具和服务。 SpringBoot导航端点 其中localhost:8080/actuator/health是健康检查端点&#xff0c;加上以下配置&#xf…