Spring Boot实现定时任务调度

在业务系统中,定时任务是非常常见的需求,例如定时对订单状态进行更新、定时生成销售报表、自动化库存管理等。Spring Boot 提供了非常方便的定时任务调度功能,并且结合线程池技术,我们可以高效地执行多个定时任务,保证系统的扩展性和高可用性。
本文将深入探讨如何使用 Spring Boot 实现定时任务调度,特别是结合线程池技术,确保多个任务能够并发安全地执行。此外,我们还将展示如何利用 cron 表达式灵活定义定时任务的触发时间,提供详细的示例以及电商交易系统中的实际应用。


1. 定时任务调度的必要性

在电商交易系统中,定时任务的应用场景非常广泛,包括但不限于以下几种:

  1. 订单状态更新:定时检测用户的支付状态,超时未支付的订单自动取消。
  2. 自动清理任务:定期清理过期的购物车信息或未完成的订单。
  3. 库存管理:定期检查库存状态,生成库存预警。
  4. 生成报表:每天、每周或每月定时生成销售报表,发送给管理人员。

为了应对这些任务,系统需要一个可靠的定时任务调度器,并且在多任务并发执行时,必须保证系统的性能和稳定性。这就需要引入线程池技术来控制并发任务的执行。


2. Spring Boot 定时任务基础

Spring Boot 提供了非常简便的定时任务支持。通过 @EnableScheduling 注解,我们可以轻松地开启定时任务调度功能,而通过 @Scheduled 注解,我们可以指定任务的执行时间和频率。

2.1 启用定时任务

首先,我们需要在 Spring Boot 应用的主类上添加 @EnableScheduling 注解,启用定时任务调度。

package com.example.ecommerce;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling
public class EcommerceApplication {public static void main(String[] args) {SpringApplication.run(EcommerceApplication.class, args);}
}

2.2 使用 @Scheduled 注解

@Scheduled 注解可以定义任务的执行时间。它支持多种形式的定时表达式,其中最灵活的方式是使用 cron 表达式。

package com.example.ecommerce.task;import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class OrderStatusCheckTask {/*** 每隔1分钟检查一次订单状态*/@Scheduled(cron = "0 */1 * * * ?")public void checkOrderStatus() {System.out.println("执行定时任务:检查订单状态");// 订单状态检查逻辑}
}

上面的示例中,checkOrderStatus() 方法每隔1分钟执行一次,cron 表达式 0 */1 * * * ? 表示每分钟的第0秒执行一次。


3. 结合线程池技术优化定时任务

Spring Boot 的定时任务调度默认是串行执行的,也就是说,如果一个任务没有执行完毕,其他定时任务将无法开始。对于电商交易系统这样复杂的场景,多任务并发执行是非常有必要的,因此我们需要结合线程池来优化定时任务的执行。

3.1 启用异步线程池

Spring 提供了 @EnableAsync 注解用于启用异步任务支持,我们可以结合线程池让多个定时任务并发执行。

首先,在配置类中启用异步支持,并配置一个自定义线程池。

package com.example.ecommerce.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;@Configuration
@EnableAsync
public class TaskSchedulerConfig {@Beanpublic ThreadPoolTaskScheduler taskScheduler() {ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.setPoolSize(10); // 线程池大小scheduler.setThreadNamePrefix("TaskScheduler-"); // 线程名称前缀scheduler.setAwaitTerminationSeconds(60); // 等待所有任务完成的最大时间scheduler.setWaitForTasksToCompleteOnShutdown(true); // 在关闭时等待任务完成return scheduler;}
}

在这里,我们定义了一个 ThreadPoolTaskScheduler,该线程池用于处理定时任务。线程池的大小设置为 10,表示最多可以同时执行 10 个定时任务。你可以根据系统的实际负载和需求调整线程池的大小。

3.2 使用线程池并发执行定时任务

接下来,我们将前面的 OrderStatusCheckTask 定时任务与线程池结合,让它在多线程环境中执行。

package com.example.ecommerce.task;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class OrderStatusCheckTask {@Autowiredprivate ThreadPoolTaskScheduler taskScheduler;/*** 每隔1分钟检查一次订单状态*/@Scheduled(cron = "0 */1 * * * ?")public void checkOrderStatus() {taskScheduler.submit(() -> {System.out.println("执行定时任务:检查订单状态");// 订单状态检查逻辑});}
}

在这个示例中,checkOrderStatus() 方法中的任务被提交给了我们自定义的线程池 taskScheduler,因此该任务将会并发执行,而不会影响其他定时任务的正常运行。


4. 支持动态配置定时任务

电商交易系统中,任务的执行时间可能需要根据实际需求进行动态调整。例如,我们希望能够通过数据库或配置文件来管理定时任务的执行时间,而不是在代码中写死。为此,我们可以引入动态配置定时任务的机制。

4.1 表结构设计

我们可以通过数据库表来保存定时任务的配置信息,并允许系统在运行时加载这些配置,动态调整任务的执行时间。

任务表 scheduled_task
CREATE TABLE scheduled_task (id BIGINT PRIMARY KEY AUTO_INCREMENT,task_name VARCHAR(255) NOT NULL COMMENT '任务名称',cron_expression VARCHAR(255) NOT NULL COMMENT 'Cron 表达式',status TINYINT NOT NULL DEFAULT 1 COMMENT '任务状态: 1-启用, 0-禁用',last_run_time TIMESTAMP COMMENT '上次运行时间',created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);

字段说明:

  • task_name:定时任务的名称。
  • cron_expression:任务的 cron 表达式。
  • status:任务的状态,1 表示启用,0 表示禁用。
  • last_run_time:任务上次运行的时间。
  • created_atupdated_at:记录任务的创建和更新时间。

4.2 动态加载任务配置

我们可以通过数据库查询定时任务的配置信息,并动态调整任务的执行时间。

package com.example.ecommerce.service;import com.example.ecommerce.model.ScheduledTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class DynamicTaskService {@Autowiredprivate TaskScheduler taskScheduler;@Autowiredprivate ScheduledTaskRepository taskRepository;/*** 加载所有启用的定时任务*/public void loadTasks() {List<ScheduledTask> tasks = taskRepository.findAllByStatus(1);for (ScheduledTask task : tasks) {taskScheduler.schedule(() -> executeTask(task), new CronTrigger(task.getCronExpression()));}}/*** 执行定时任务*/private void executeTask(ScheduledTask task) {System.out.println("执行任务:" + task.getTaskName());// 执行具体任务逻辑}
}

在这个服务中,我们从数据库中加载所有启用的定时任务,并通过 taskScheduler.schedule() 方法使用 cron 表达式动态调度这些任务。

4.3 定时任务的动态控制

我们还可以通过 REST 接口或者管理后台来动态控制任务的启用和禁用。通过更新数据库中的 status 字段,就可以控制任务是否执行。定时任务调度器会根据最新的配置动态调整任务的执行状态。


5. 电商交易系统中的定时任务场景示例

5.1 订单状态检查定时任务

在电商交易系统中,订单的状态管理是非常重要的。用户下单后,如果在一定时间内未支付订单,需要自动取消订单。我们可以通过定时任务来实现这个功能。

package com.example.ecommerce.task;import com.example.ecommerce.model.Order;
import com.example.ecommerce.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class OrderStatusCheckTask {@Autowiredprivate OrderService orderService;/*** 每隔5分钟检查一次未支付订单状态*/@Scheduled(cron = "0 */5 * * * ?")public void checkUnpaidOrders() {System.out.println("执行定时任务:检查未支付订单");List<Order> unpaidOrders = orderService.getUnpaidOrders();for (Order order : unpaidOrders) {if (order.isOverdue()) {orderService.cancelOrder(order);System.out.println("取消超时未支付订单:" + order.getId());}}}
}

通过这个定时任务,我们可以每隔 5 分钟检查一次未支付的订单,并自动取消超时未支付的订单。

5.2 库存预警定时任务

对于库存管理,电商系统可以设置一个定时任务,每天检查库存状态,并生成库存预警,提醒管理人员补充库存。

package com.example.ecommerce.task;import com.example.ecommerce.model.Product;
import com.example.ecommerce.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class InventoryCheckTask {@Autowiredprivate InventoryService inventoryService;/*** 每天早上8点检查库存状态*/@Scheduled(cron = "0 0 8 * * ?")public void checkInventory() {System.out.println("执行定时任务:检查库存状态");List<Product> lowStockProducts = inventoryService.getLowStockProducts();for (Product product : lowStockProducts) {System.out.println("库存不足产品:" + product.getName());// 发送库存预警}}
}

这个任务每天早上8点执行,检查库存状态,并生成库存预警,确保电商平台的库存充足。


6. 异步和并发控制

为了避免定时任务之间互相影响,或者由于单个任务执行过长导致的阻塞,我们可以将每个定时任务放到线程池中异步执行,从而提高任务的执行效率和系统的响应能力。

通过将定时任务提交给线程池执行,即使某个任务耗时较长,也不会影响其他任务的正常执行。


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

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

相关文章

C++笔记21•C++11的新特性•

相比于 C98/03&#xff0c;C11则带来了数量可观的变化&#xff0c;其中包含了约140个新特性&#xff0c;以及对C03标准中约600个缺陷的修正&#xff0c;这使得C11更像是从C98/03中孕育出的一种新语言。相比较而言&#xff0c;C11能更好地用于系统开发和库开发、语法更加泛华和简…

如何合并pdf文件,四款软件,三步搞定!

在数字化办公的浪潮中&#xff0c;PDF文档因其跨平台兼容性和安全性&#xff0c;成为了我们日常工作中不可或缺的一部分。然而&#xff0c;面对多个PDF文件需要整合成一个文件时&#xff0c;不少小伙伴可能会感到头疼。别担心&#xff0c;今天我们就来揭秘四款高效PDF合并软件&…

演示:基于WPF的DrawingVisual开发的Chart图表和表格绘制

一、目的&#xff1a;基于WPF的DrawingVisual开发的Chart图表和表格绘制 二、预览 钻井井轨迹表格数据演示示例&#xff08;应用Table布局&#xff0c;模拟井轨迹深度的绘制&#xff09; 饼图表格数据演示示例&#xff08;应用Table布局&#xff0c;模拟多个饼状图组合显示&am…

尚品汇-秒杀商品定时任务存入缓存、Redis发布订阅实现状态位(五十一)

目录&#xff1a; &#xff08;1&#xff09;秒杀业务分析 &#xff08;2&#xff09;搭建秒杀模块 &#xff08;3&#xff09;秒杀商品导入缓存 &#xff08;4&#xff09;redis发布与订阅实现 &#xff08;1&#xff09;秒杀业务分析 需求分析 所谓“秒杀”&#xff0…

又到了金九银十,你的简历写好了吗?

又到了金九银十的招聘季&#xff0c;不过这几年求职环境越来越差&#xff0c;相比于跳槽找新机会&#xff0c;大家可能更倾向于守住自己手头的工作&#xff0c;稳字当头。当然&#xff0c;也有很多工作实在干烦了的朋友&#xff0c;想要换个新赛道试试。今天就给大家带来一个新…

django实现开发、测试、生产环境配置区分

文章目录 一、为什么要区分开发 (dev)、测试 (test) 和生产 (prod) 环境二、django项目如何通过配置实现环境配置的区分1、针对不同的环境创建不同的设置文件settings.py2、在设置文件中根据需要进行配置区分3、根据不同的环境运行使用不同的设置文件 任何实际的软件项目中都要…

【中级通信工程师】终端与业务(二):终端产品

【零基础3天通关中级通信工程师】 终端与业务(二)&#xff1a;终端产品 本文是中级通信工程师考试《终端与业务》科目第二章《终端产品》的复习资料和真题汇总。终端与业务是通信考试里最简单的科目&#xff0c;有效复习通过率可达90%以上&#xff0c;本文结合了高频考点和近几…

医学数据分析实训 项目三 关联规则分析作业--在线购物车分析--痹症方剂用药规律分析

文章目录 项目三 关联规则分析一、实践目的二、实践平台三、实践内容任务一&#xff1a;在线购物车分析&#xff08;一&#xff09;数据读入&#xff08;二&#xff09;数据理解&#xff08;三&#xff09;数据预处理&#xff08;四&#xff09;生成频繁项集&#xff08;五&…

基于微信小程序的美食外卖管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

分享两个虚拟试衣工具,一个在线,一个离线,还有ComfyUI插件

SAM &#xff0c;对不住了&#xff01; 我没记错的话&#xff0c;OpenAI CEO&#xff0c;性别男&#xff0c;取向男&#xff0c;配偶男。 这又让我联想到了苹果CEO库克... 所以OpenAI和Apple可以一啪即合。 钢铁直男老马就和他们都不对付~~ 开个玩笑&#xff0c;聊…

WebGL入门(一)绘制一个点

源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><scr…

Web+Mysql——MyBatis

MyBatis 目标 能够完成Mybatis代理方式查询数据能够理解Mybatis核心配置文件的配置 1&#xff0c;Mybatis 1.1 Mybatis概述 1.1.1 Mybatis概念 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由…

高等代数笔记(2)————(弱/强)数学归纳法

数学归纳法的引入情景其实很简单&#xff0c;就是多米诺骨牌。 推倒所有多米诺骨牌的关键就是推倒第一块&#xff0c;以及确保第一块倒下后会带动第二块&#xff0c;第二块带动第三块&#xff0c;以此类推&#xff0c;也就是可以递推。由此我们可以归纳出所有的多米诺骨牌都可…

MySQL学习(索引)

文章目录 基本概念单列索引普通索引&#xff08;index&#xff09;唯一索引&#xff08;unique&#xff09;主键索引 组合索引全文索引&#xff08;fulltext&#xff09;空间索引&#xff08;spatial&#xff09;MySQL存储引擎 基本概念 通过某种算法&#xff0c;构建数据模型&…

LeetCode 2374.边积分最高的节点:模拟

【LetMeFly】2374.边积分最高的节点&#xff1a;模拟 力扣题目链接&#xff1a;https://leetcode.cn/problems/node-with-highest-edge-score/ 给你一个有向图&#xff0c;图中有 n 个节点&#xff0c;节点编号从 0 到 n - 1 &#xff0c;其中每个节点都 恰有一条 出边。 图…

k8s中pod的创建过程和阶段状态

管理k8s集群 kubectl k8s中有两种用户 一种是登录的 一种是/sbin/nologin linux可以用密码登录&#xff0c;也可以用证书登录 k8s只能用证书登录 谁拿到这个证书&#xff0c;谁就可以管理集群 在k8s中&#xff0c;所有节点都被网络组件calico设置了路由和通信 所以pod的ip是可以…

如何使用 maxwell 同步到 redis?

文章目录 1、MaxwellListener2、MxwObject1. 使用Maxwell捕获MySQL变更2. 将Maxwell的输出连接到消息系统3. 从消息系统读取数据并同步到Redis注意事项 1、MaxwellListener package com.atguigu.tingshu.album.listener;import com.alibaba.fastjson.JSON; import org.apache.…

mysql中的json查询

首先来构造数据 查询department里面name等于研发部的数据 查询语句跟普通的sql语句差不多&#xff0c;也就是字段名要用到path表达式 select * from user u where u.department->$.name 研发部 模糊查询 select * from user u where u.department->$.name like %研发%…

Go-知识recover

Go-知识recover 1. 介绍2. 工作机制2.1 recover 定义2.2 工作流程2.3 总结 3. 原理3.1 recover函数的真正逻辑3.2 恢复逻辑3.3 生效条件 4. 总结4.1 recover的返回值是什么&#xff1f;4.2 执行recover之后程序将从哪里继续运行&#xff1f;4.3 recover为什么一定要在defer中使…

无法删除选定的端口,不支持请求【笔记】

场景&#xff1a;在删除打印机端口时&#xff0c;提示&#xff1a;“无法删除选定的端口&#xff0c;不支持请求”&#xff0c;如下图所示。 以下以删除USB036端口为示例&#xff0c;操作步骤如下&#xff1a; 在注册表编辑器中&#xff0c;从以下注册表项中“计算机\HKEY_LO…