Springboot使用ThreadPoolTaskScheduler轻量级多线程定时任务框架

简介: Spring注解定时任务使用不是很灵活,如果想要灵活的配置定时任务,可以使用xxl-job 或者 quartz等定时任务框架,但是过于繁琐,可能成本较大。所以可以使用ThreadPoolTaskScheduler来灵活处理定时任务

ThreadPoolTaskScheduler是什么

ThreadPoolTaskScheduler 是 Spring Framework 中的一部分,主要用于调度任务。它基于线程池,可以处理异步任务和定时任务

主要api

  • schedule(Runnable task, Trigger trigger) corn表达式,周期执行
  • schedule(Runnable task, Date startTime) 定时执行
  • scheduleAtFixedRate(Runnable task, Date startTime, long period)
    定时周期间隔时间执行。间隔时间单位 TimeUnit.MILLISECONDS
  • scheduleAtFixedRate(Runnable task, long period) 间隔时间以固定速率执行。单位毫秒

固定速率执行不会管上次执行的状态如何

在使用前需要配置下ThreadPoolTaskScheduler

@Configuration
public class SchedulingTaskConfig {@Bean(name = "myThreadPoolTaskScheduler")public ThreadPoolTaskScheduler threadPoolTaskScheduler(){ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();taskScheduler.setPoolSize(60);taskScheduler.setThreadNamePrefix("task-");taskScheduler.setAwaitTerminationSeconds(3000);taskScheduler.setWaitForTasksToCompleteOnShutdown(true);return taskScheduler;}
}

cron表达式

@Overridepublic String startTask() {ScheduledFuture<?> schedule = myThreadPoolTaskScheduler.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1s执行一次");}}, new CronTrigger("0/1 * * * * ?"));

定时执行一次

 myThreadPoolTaskScheduler.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行");}},new Date(System.currentTimeMillis() + 3000));

在固定时间以固定速率执行

 myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行,固定3s执行一次");}},new Date(System.currentTimeMillis() + 3000),3000);

任务取消

private  ScheduledFuture<?> schedule ;@Overridepublic String startTask() {schedule = myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行,固定3s执行一次");}}, new Date(System.currentTimeMillis() + 3000), 3000);return "开启成功";}@Overridepublic String stopTask() {if (schedule != null){schedule.cancel(true);System.out.println("任务取消成功");return "取消成功";}return "取消失败";}

实现页面控制定时任务开关

将定时任务保存到数据库中,并在页面上实现定时任务的开关,以及更新定时任务时间后重新创建定时任务

数据库实体

@TableName("task")
@Data
public class ScheduleTask {public interface Update{};@TableId(type = IdType.AUTO)@NotNull(message = "任务id不能为空",groups = Update.class)private Integer id;@NotBlank(message = "请填写任务执行类")@TableField("task_clazz")private String taskClazz;@NotBlank(message = "请填写任务执行方法")@TableField("task_method")private String taskMethod;@NotBlank(message = "请填写任务执行时间,采用cron格式")@TableField("cron")private String cron;@TableLogic@TableField("status")private Integer status;
}

contrloller

@RestController
@RequiredArgsConstructor
public class TaskManagerController {private final TaskManagerService taskManagerService;@PostMapping("/addTask")public Boolean addTask(@RequestBody @Validated ScheduleTask task){return taskManagerService.addTask(task);}@PostMapping("/stopTask/{id}")public Boolean stopTask(@PathVariable Integer id){return taskManagerService.stopTask(id);}
}

service

@Service
@Slf4j
@RequiredArgsConstructor
public class TaskManagerServiceImpl implements TaskManagerService {private final ScheduleTaskMapper scheduleTaskMapper;private final TaskManager taskManager;@Overridepublic Boolean addTask(ScheduleTask task) {int i = scheduleTaskMapper.insert(task);if (i > 0){TaskRunnable taskRunnable = new TaskRunnable(task.getTaskClazz(), task.getTaskMethod());taskManager.addTask(String.valueOf(task.getId()),taskRunnable,task.getCron());return true;}return false;}@Overridepublic Boolean stopTask(Integer id) {int i = scheduleTaskMapper.deleteById(id);if (i> 0){taskManager.stopTask(String.valueOf(id));return true;}return false;}
}

TaskRunnable

通过此类获取具体的执行方法

@Slf4j
public class TaskRunnable implements Runnable{/*** 定时任务类*/private final String clazz;/*** 定时任务方法*/private final String methodName;public TaskRunnable(String clazz, String methodName) {this.clazz = clazz;this.methodName = methodName;}@Overridepublic void run() {try {//获取类Object bean = SpringContextUtils.getBean(clazz);//获取方法Method method = bean.getClass().getDeclaredMethod(methodName);//设置方法可用ReflectionUtils.makeAccessible(method);//执行方法method.invoke(bean);} catch (Exception e) {log.error("获取方法信息报错:{}",e.getMessage());throw new RuntimeException(e);}}
}

任务调度类

@Component
@RequiredArgsConstructor
@Slf4j
public class TaskManager {private final ThreadPoolTaskScheduler myThreadPoolTaskScheduler;public static ConcurrentHashMap<String, ScheduledFuture<?>> cache = new ConcurrentHashMap<>();/*** 创建定时任务* @param key 任务key* @param taskRunnable 当前线程* @param cron 定时任务cron*/public void addTask(String key ,TaskRunnable taskRunnable ,String cron){//取消任务this.stopTask(key);ScheduledFuture<?> schedule = myThreadPoolTaskScheduler.schedule(taskRunnable, new CronTrigger(cron));if (schedule != null){cache.put(key,schedule);log.info("当key为{}的定时任务创建成功",key);}}public void stopTask(String key){if (cache.get(key) == null){log.info("当前没有key为{}的定时任务",key);return;}ScheduledFuture<?> scheduledFuture = cache.get(key);if (scheduledFuture != null){scheduledFuture.cancel(true);cache.remove(key);log.info("当前key为{}的定时任务已取消",key);}}
}

工具类

@Component
public class SpringContextUtils implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {SpringContextUtils.context = applicationContext;}public static Object getBean(String name){return context.getBean(name);}
}

方法测试


@Slf4j
@Component(value = "testTask")
public class TestTask {public void taskMethod(){log.info(String.format("调用了当前定时任务"));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2024 ICPC ShaanXi Provincial Contest —— C. Seats(个人理解)拓扑+dfs

2024 ICPC ShaanXi Provincial Contest —— C. Seats&#xff08;个人理解&#xff09;拓扑dfs 先放个传送门 https://codeforces.com/gym/105257/problem/C ———————————————————————————————————— 思路 可以看到&#xff0c;每一个编…

Vision Based Navigation :针对航天领域的基于视觉导航机器学习应用生成训练数据集

2024-09-18 由欧洲空间局主导&#xff0c;由空客防务与空间公司参与创建Vision Based Navigation &#xff0c; 为空间任务中的基于视觉导航&#xff08;VBN&#xff09;机器学习应用生成训练数据集。 目前遇到的困难和挑战 1、数据集的可用性和充分性&#xff1a; 挑战&#x…

BFS 解决多源最短路问题

文章目录 多源BFS542. 01 矩阵题目解析算法原理代码实现 1020. 飞地的数量题目解析算法原理 1765. 地图中的最高点题目解析算法原理代码实现 1162. 地图分析题目解析算法原理代码实现 多源BFS 单源最短路&#xff1a; 一个起点、一个终点 多源最短路&#xff1a; 可以多个起点…

9.安卓逆向-安卓开发基础-安卓四大组件2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

[云服务器13] 如何正确选择云服务器?

【非广告&#xff0c;仅提供建议&#xff0c;没有强制消费引导】 这期我们不讲搭建教程了&#xff0c;因为我想到前面12篇的教程&#xff0c;有关套餐配置的教程好像都有点敷衍…… 所以这期我们主要来说一说服务器的配置选择和不同配置的应用场景。 网站: 雨云 打开后&…

基于ZigBee的农业大棚信息采集系统设计

过去的农业大棚种植中大多需要依靠经验来实现浇水施肥等工作&#xff0c;无法根据天气的变化做出顺应的改变&#xff0c;也就造成了大棚内种植农作物的产量和质量很难得到保证。伴随着物联网与农业种植的结合&#xff0c;基于ZigBee通信和传感器等技术开发一款能监测大棚内环境…

Linux:路径末尾加/和不加/的区别

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 普通文件操作 首先说明这个问题只会出现在目录和符号链接中&#xff0c;因为如果想要索引普通文件但却在路径末尾加/则会出现错误&#xff0c;如例1所示。 # 例1 zhang…

free源码

文章目录 free源码调试main_arena结构&#xff1a;free_hooktcachetcache的结构free_chunk进入tcache&#xff1a; fastbinunlink 合并top free源码调试 main_arena结构&#xff1a; 整体看一下main_arena的结构&#xff1a; free_hook free_hook&#xff0c;在glibc-2.3…

在 Windows 11 中,可以通过修改注册表来更改系统的自动更新时间设置

regedit 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings FlightSettingsMaxPauseDays 36524

AI少女/HS2甜心选择2 仿崩铁人物卡全合集打包

内含AI少女/甜心选择2 仿崩铁【崩坏 星穹铁道 】角色卡全合集打包共6张 内含&#xff1a;布洛妮娅镜流卡芙卡希儿停云银狼 下载地址&#xff1a; https://www.51888w.com/375.html 部分演示图&#xff1a;

项目第六弹:虚拟机管理模块、路由匹配模块

项目第六弹&#xff1a;虚拟机管理模块、路由匹配模块 一、虚拟机管理模块的设计1.什么是虚拟机&#xff1f;2.借助MySQL来理解一下3.如何整合&#xff1f;【埋下伏笔】 二、RabbitMQ为何要有虚拟机1.从业务角度来讲2.步步探索1.优点2.结合业务适用场景和需求3.发掘真正的原因4…

NoSql数据库Redis知识点

数据库的分类 关系型数据库 &#xff0c;是建立在关系模型基础上的数据库&#xff0c;其借助于集合代数等数学概念和方法来处理数据库 中的数据主流的 MySQL 、 Oracle 、 MS SQL Server 和 DB2 都属于这类传统数据库。 NoSQL 数据库 &#xff0c;全称为 Not Only SQL &a…

Python学习——【4.1】数据容器:list列表

文章目录 【4.1】数据容器&#xff1a;list列表一、数据容器入门二、数据容器&#xff1a;list 列表&#xff08;一&#xff09;列表的定义&#xff08;二&#xff09;列表的下标索引&#xff08;三&#xff09;列表的常用操作&#xff08;1&#xff09;列表的查询功能&#xf…

RAG+Agent人工智能平台:RAGflow实现GraphRA知识库问答,打造极致多模态问答与AI编排流体验

1.RAGflow简介 全面优化的 RAG 工作流可以支持从个人应用乃至超大型企业的各类生态系统。大语言模型 LLM 以及向量模型均支持配置。基于多路召回、融合重排序。提供易用的 API&#xff0c;可以轻松集成到各类企业系统。支持丰富的文件类型&#xff0c;包括 Word 文档、PPT、exc…

Vue3入门 - ElementPlus中左侧菜单和Tabs菜单组合联动效果

在Vue3中&#xff0c;ElementPlus是使用比较广泛的UI组件库&#xff0c;提供了丰富的界面元素支持项目开发需求。在后台管理系统中&#xff0c;左侧或顶部的菜单栏通常包含多个子菜单项&#xff0c;通过菜单的展开和收缩功能&#xff0c;用户可以方便地查看或隐藏不需要的菜单项…

数字世界中的浪漫:揭秘会跳动的爱心

在编程的世界里&#xff0c;复杂的技术可以与艺术产生美妙的碰撞。无论是通过代码实现动态效果&#xff0c;还是用算法绘制图案&#xff0c;程序员都可以成为数字艺术的创作者。而今天&#xff0c;我们将通过 Python 的强大 GUI 工具库 Tkinter&#xff0c;用简单的代码生成一颗…

U-Boot顶层Makefile详解

直接参考【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81 本文仅作为个人笔记使用&#xff0c;方便进一步记录自己的实践总结。 上一章我们详细的讲解了 uboot 的使用方法&#xff0c;其实就是各种命令的使用&#xff0c;学会 uboot 使用以后就可以尝试移植 uboot 到自己的开发…

linux操作系统的基本命令

1.linux下的文件系统 在linux操作目录下没有像window操作系统下盘符的概念,只有一个根目录/,所有文件目录都在它的下面 linux的目录结构: 在Linux系统中: 文件都从跟目录开始的,用/表示文件名称区分大小写路径都是以/俩进行分隔(windown用\分隔)以.开头的文件为隐藏文件 Li…

鸿蒙开发之ArkUI 界面篇 十七 购物综合案例

layoutWeight:子元素与兄弟元素主轴方向按照权重进行分配,参数是联合类型&#xff0c;数字或者是字符串&#xff0c;在指定的空间占多少份额&#xff0c;数字越大&#xff0c;表示在空间中占用的份额越多&#xff0c;如果父容器的子组件没有别的指定&#xff0c;剩下的空间全部…

10分钟一条童装走秀爆款Ai视频,小白轻松上手,新蓝海赛道,竞争小机会多!

今天我要给大家带来一个超级有趣的项目——童装走秀AI视频制作。 这不仅是一个充满创意的项目&#xff0c;而且操作简单&#xff0c;即使是视频制作的新手也能轻松上手。 更重要的是&#xff0c;这个项目竞争小&#xff0c;变现机会多&#xff0c;是进入新蓝海赛道的绝佳机会…