定时任务还可以这么玩--基于SpringBoot中的ThreadPoolTaskScheduler实现动态定时任务

Hi,大家好,我是抢老婆酸奶的小肥仔。

最近做了一个需求:将定时任务保存到数据库中,并在页面上实现定时任务的开关,以及更新定时任务时间后重新创建定时任务。

于是想到了SpringBoot中自带的ThreadPoolTaskScheduler。

在SpringBoot中提供的ThreadPoolTaskScheduler这个类,该类提供了一个schedule(Runnable task, Trigger trigger)的方法可以实现定时任务的创建,该方法是通过管理线程来实现。

schedule(Runnable task, Trigger trigger)源码:

public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {ScheduledExecutorService executor = getScheduledExecutor();try {ErrorHandler errorHandler = this.errorHandler;if (errorHandler == null) {errorHandler = TaskUtils.getDefaultErrorHandler(true);}return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule();}catch (RejectedExecutionException ex) {throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);}}

上述源码中,首先会获取定时任务执行服务,即:ScheduledExecutorService executor = getScheduledExecutor(); ,然后创建重排线程类,并调用schedule() 方法来创建ScheduledFuture<?>

ScheduledFuture<?> 中提供了cancel(boolean mayInterruptIfRunning) 来实现定时任务的删除。通过这两个方法,我们可以实现上述的需求。

废话不多说,代码撸起。

1、代码实现

1.1 定时任务线程配置

/*** @author: jiangjs* @description: * @date: 2023/2/17 9:51**/
@Configuration
public class SchedulingTaskConfig {@Bean(name = "taskSchedulerPool")public ThreadPoolTaskScheduler threadPoolTaskScheduler(){ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();//设置线程池大小taskScheduler.setPoolSize(60);//线程名称前缀taskScheduler.setThreadNamePrefix("task-pool-");//设置终止时等待最大时间,单位s,即在关闭时,需等待其他任务完成执行taskScheduler.setAwaitTerminationSeconds(3000);//设置关机时是否等待计划任务完成,不中断正在运行的任务并执行队列中的所有任务,默认falsetaskScheduler.setWaitForTasksToCompleteOnShutdown(true);return taskScheduler;}
}

1.2 获取类工具

/*** @author: jiangjs* @description: 上下文获取类* @date: 2023/1/30 10:28**/
@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);}
}

通过ApplicationContext中getBean通过类名来获取对应的类。

1.3 创建定时任务线程类

由于ThreadPoolTaskScheduler是基于线程来创建定时任务的,因此我们封装一个类来实现Runnable,其主要作用是获取数据参数,绑定定时任务类及定时任务方法,然后在通过反射拿到方法进行执行。参数则定义成泛型,便于直接传递,定时任务方法获取后不需要再次转换。

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Objects;
/*** @author: jiangjs* @description: 实现定时任务线程* @date: 2023/2/16 15:31**/
@Slf4j
public class SchedulingTaskRunnable<T> implements Runnable {/*** 其他参数*/private final T t;/*** 定时任务类*/private final String clazz;/*** 定时任务方法*/private final String methodName;SchedulingTaskRunnable(T t,String clazz,String methodName){this.t = t;this.clazz = clazz;this.methodName = methodName;}@Overridepublic void run() {Object bean = SpringContextUtils.getBean(clazz);Method method;try{method = Objects.nonNull(t) ? bean.getClass().getDeclaredMethod(methodName,t.getClass()) : bean.getClass().getDeclaredMethod(methodName);ReflectionUtils.makeAccessible(method);if (Objects.nonNull(t)){method.invoke(bean,t);} else {method.invoke(bean);}}catch (Exception e){log.error("获取方法信息报错:{}",e.getMessage());}}
}

1.4 封装管理定时任务工具

该工具主要实现定时任务的创建和删除方法,在创建定时任务时要先调用删除方法,确保当前任务是唯一的,因此在更新定时任务时间时,只需要调用创建方法即可。

其中定义了一个 ConcurrentHashMap<String, ScheduledFuture<?>> ,主要作用是为了管理定时任务,通过自定义的任务名称或Id,获取到ScheduledFuture\<?>来进行相关操作,例如:调用关闭方法。

    import lombok.extern.slf4j.Slf4j;import org.apache.poi.ss.formula.functions.T;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;import org.springframework.scheduling.support.CronTrigger;import org.springframework.stereotype.Component;import java.util.Objects;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ScheduledFuture;/*** @author: jiangjs* @description: 定时任务管理方法* @date: 2023/2/16 15:48**/@Component@Slf4jpublic class SchedulingTaskManage {/*** 将任务放入map便于管理*/private final ConcurrentHashMap<String, ScheduledFuture<?>> cache = new ConcurrentHashMap<>();@Resource(name = "taskSchedulerPool")private ThreadPoolTaskScheduler threadPoolTaskScheduler;/*** 删除定时任务* @param key 自定义定时任务名称*/public void stopSchedulingTask(String key){if (Objects.isNull(cache.get(key))){log.info(String.format(".......当前key【%s】没有定时任务......",key));return;}ScheduledFuture<?> future = cache.get(key);if (Objects.nonNull(future)){//关闭当前定时任务future.cancel(Boolean.TRUE);cache.remove(key);log.info(String.format("删除【%s】对应定时任务成功",key));}}/*** 创建定时任务* @param key 任务key* @param runnable 当前线程* @param cron 定时任务cron*/public void createSchedulingTask(String key,SchedulingTaskRunnable<T> runnable, String cron){ScheduledFuture<?> schedule = taskScheduler.schedule(runnable, new CronTrigger(cron));assert schedule != null;cache.put(key,schedule);log.info(String.format("【%s】创建定时任务成功",key));}}

2、测试

创建的线程类与工具已经封装完成,接下来我们来进行下测试。

先创建定时任务表:

create table t_schedule_task(id int auto_increment primary key not null comment '主键Id',task_clazz varchar(200) not null comment '定时任务类',task_method varchar(200) not null comment '定时任务执行方法',cron varchar(200) not null comment '定时任务时间',status smallint not null default 0 comment '定时任务状态,0:开启,1:关闭'
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 comment '定时任务管理表';

2.1 创建定时任务执行类

    /*** @author: jiangjs* @description:* @date: 2023/2/16 16:24**/@Slf4j@Component(value = "testSchedulingTask")public class TestSchedulingTask {public void taskMethod(UserInfo obj){log.info(String.format("调用了当前定时任务:输出参数:参数1:%s,参数2:%s",obj.getUserName(),obj.getPassword()));}}

简单获取用户信息,进行显示。

2.2 创建实现方法

    /*** @author: jiangjs* @description:* @date: 2023/1/12 10:53**/@Servicepublic class ScheduledTaskManageServiceImpl implements ScheduledTaskManageService {@Autowiredprivate SchedulingTaskManage taskManage;@Resourceprivate ScheduleTaskMapper scheduleTaskMapper;@Overridepublic ResultUtil<?> addTask(ScheduleTask task) {UserInfo userInfo = new UserInfo();userInfo.setUserName("张三");userInfo.setPassword("121212121212");String cron = task.getCron();boolean validExpression = CronExpression.isValidExpression(cron);if (!validExpression){return ResultUtil.error("无效的cron格式,请重新填写");}scheduleTaskMapper.insert(task);SchedulingTaskRunnable<UserInfo> taskRunnable = new SchedulingTaskRunnable<>(userInfo, task.getTaskClazz(), task.getTaskMethod());taskManage.createSchedulingTask(String.valueOf(task.getId()), taskRunnable,task.getCron());return ResultUtil.success();}@Overridepublic ResultUtil<?> deleteTask(Integer id) {scheduleTaskMapper.deleteById(id);taskManage.stopSchedulingTask(String.valueOf(id));return ResultUtil.success();}@Overridepublic ResultUtil<?> editTask(ScheduleTask task) {scheduleTaskMapper.updateById(task);UserInfo userInfo = new UserInfo();userInfo.setUserName("张三");userInfo.setPassword("33333333");SchedulingTaskRunnable<UserInfo> taskRunnable = new SchedulingTaskRunnable<>(userInfo, task.getTaskClazz(), task.getTaskMethod());taskManage.createSchedulingTask(String.valueOf(task.getId()), taskRunnable,task.getCron());return ResultUtil.success();}}

上述代码中ScheduleTaskMapper是继承Mybatis-plus中的BaseMapper实现单表操作,小伙伴们可自行实现。

2.3 执行结果

我们创建了三个方法,分别是: addTask,editTask,deleteTask来实现定时任务的删除,添加,具体实现参考上面代码。看看执行结果:

创建定时任务:
提交参数:

image.png

taskClazz:对应测试类 @Component(value = “testSchedulingTask”)中的value值
taskMethod:测试内中的执行方法。如:TestSchedulingTask中的taskMethod方法
cron:定时任务的cron格式时间

从执行结果动态图可以看到,任务每隔5s种就会获取一次用户信息。

删除定时任务:

image.png

删除Id为1000的数据库任务,同时也是删除key为1000的定时任务

任务被删除后,即使过了5s依然没有任务在执行。


总结

ThreadPoolTaskScheduler实现定时任务主要是通过对线程的管理来进行操作,添加任务时即创建一个线程,删除时即将该线程删除。因此在创建定时任务只需要创建线程就可以,在创建线程时,通过反射来获取对应的方法及传递参数。

上述就是使用SprngBoot中的ThreadPoolTaskScheduler来实现定时任务,我们只要使用前端连接相应的接口就可以实现管理人员管理定时任务的功能。

今天就分享这么多,谢谢大家听我叨叨。

源码地址:https://github.com/lovejiashn/schedule_task.git

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

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

相关文章

渗透测试工具--AppInfoScanner 的安装与使用(一)

简洁 ApplicationScanner是一个快速稳定的App代码扫描工具&#xff0c;其主要功能是对ipa和apk文件进行扫描&#xff0c;以快速发现存在风险的代码。这款工具基于Python 3.7实现其主要功能&#xff0c;同时apk检测部分需要JDK 11的支持&#xff0c;因此它具备较好的跨平台特性…

旺店通·企业奇门与金蝶云星空对接集成订单查询打通销售订单新增

旺店通企业奇门与金蝶云星空对接集成订单查询打通销售订单新增 对接源平台:旺店通企业奇门 慧策最先以旺店通ERP切入商家核心管理痛点——订单管理&#xff0c;之后围绕电商经营管理中的核心管理诉求&#xff0c;先后布局流量获取、会员管理、仓库管理等其他重要经营模块。慧策…

邮件群发还能用吗

邮件群发仍然可以使用。不过&#xff0c;在进行邮件群发时&#xff0c;可能会遇到一些问题&#xff0c;如选择合适的邮件群发软件、应对垃圾邮件过滤器的挑战、管理收件人列表、邮件内容的个性化和定制、邮件投递的时间管理以及避免被列入黑名单等。 为了优化邮件群发的效果&a…

五分钟解决Springboot整合Mybaties

SpringBoot整合Mybaties 创建maven工程整合mybaties逆向代码生成 创建maven工程 1.通过idea创建maven工程如下图 2.生成的工程如下 以上我们就完成了一个maven工程&#xff0c;接下来我们改造成springboot项目。 这里主要分为三步&#xff1a;添加依赖&#xff0c;增加配置&…

Oracle-一次TX行锁堵塞事件

问题背景&#xff1a; 接用户问题报障&#xff0c;应用服务出现大量会话堆积现象&#xff0c;数据库锁堵塞严重&#xff0c;需要协助进行问题定位和排除。 问题分析&#xff1a; 登录到数据库服务器上&#xff0c;首先查看一下数据库当前的等待事件情况&#xff0c;通过gv$ses…

SQLSERVER CPU占用过高的优化

有同事反应服务器CPU过高&#xff0c;一看截图基本都是100%了&#xff0c;my god&#xff0c;这可是大问题&#xff0c;赶紧先看看。 让同事查看系统进程&#xff0c;发现是SQLServer的CPU占用比较高。首先想到的是不是报表生成的时候高&#xff0c;因为这块之前出现过问题&…

实例分割——水下垃圾数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

基于大模型的idea提炼:围绕论文和引用提炼idea之ResearchAgent

前言 对本博客比较熟悉的朋友知道&#xff0c;我司论文项目组正在基于大模型做论文的审稿(含CS英文论文审稿、和金融中文论文审稿)、翻译&#xff0c;且除了审稿翻译之外&#xff0c;我们还将继续做润色/修订、idea提炼(包含论文检索)&#xff0c;是一个大的系统&#xff0c;包…

RN封装的toast提示框组件

import React, {useState} from react; import {View,Text,Modal,ActivityIndicator,StyleSheet,TouchableOpacity,Button, } from react-native;// 接收toaseflag为是否显示toast // title为提示内容 const ToastModal ({toastflag, title}) > {return (<Modal animati…

选修选课|基于Springboot+vue的大学生选修选课系统的设计与实现(源码+数据库+文档)

大学生选修选课系统 目录 基于Springboot&#xff0b;vue的大学生选修选课系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1用户信息管理 2 课程信息管理 3排课信息管理 4公告信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题…

落地护眼灯十大品牌哪款性价比高?品牌排行榜前十名全面揭晓!

落地护眼灯十大品牌哪款性价比高&#xff1f;落地护眼灯已经逐渐成为孩子日常使用率较高的电器之一&#xff0c;它的优点非常突出&#xff0c;对于学习、工作、绘画等环境都能够提供良好的健康环境&#xff0c;同时还携带多种智能调节功能&#xff0c;例如&#xff1a;入座感应…

反汇编一个ARM64的机器码

文章目录 使用objdump直接阅读ARM64手册使用反汇编网站 有下面一个机器码&#xff1a;0x929ffee9&#xff0c;如何翻译成汇编呢&#xff1f; 下面介绍几种做法&#xff1a; 使用objdump 将这个机器码写到文件中&#xff0c;然后使用objdump去反汇编 创建一个二进制文件 dd…

言出身随!人情世故:利益交换与人脉的重要性——早读(逆天打工人爬取热门微信文章解读)

巴黎输了&#xff0c;看了比赛还得加班 引言Python 代码第一篇 洞见 认知越高的人&#xff0c;越懂得感恩第二篇 冯站长之家 2024年5月8日&#xff08;周三&#xff09;三分钟新闻早餐结尾 智慧赋予我决策的明灯 勇气则是我行动的盾牌 在细雨中骑行 是我以智慧选择的道路 用勇气…

数据结构复习/学习9--堆/堆实现/升降序建堆/top-k问题

一、堆与完全二叉树 1.堆的逻辑与物理结构 2.父节点与子节点的下标 3.大小根堆 二、堆的实现&#xff08;大根堆为例&#xff09; 注意事项总结&#xff1a; 注意堆中插入与删除数据的位置和方法与维持大根堆有序时的数据上下调整 三、堆排序 1.排升序建大堆效率高 注意事项…

CST电磁仿真查看模型的截面结构和生成Spice模型【入门教程】

通过Logfile查看仿真统计 一次性了解仿真统计! Post-Processing > Manage Results > Logfile 利用CPU Threads、Mesh Cells、Time Steps以及Total Solver Time等Logfile&#xff0c;可以一目了然地了解仿真统计。 &#xff08;1&#xff09;点击Post-Processing选项卡…

Ryght 在 Hugging Face 专家助力下赋能医疗保健和生命科学之旅

本文是 Ryght 团队的客座博文。 Ryght 是何方神圣&#xff1f; Ryght 的使命是构建一个专为医疗保健和生命科学领域量身定制的企业级生成式人工智能平台。最近&#xff0c;公司正式公开了 Ryght 预览版 平台。 Ryght 预览版https://www.ryght.ai/signup?utm_campaignPreview%2…

对于习惯使用ftp传输的企业,如何寻找最佳的替代方案?

FTP协议广泛应用各行业的文件传输场景中&#xff0c;对于很多企业而言&#xff0c;由于FTP应用获取门槛低、使用普遍&#xff0c;因此&#xff0c;有较为稳定的FTP使用习惯&#xff0c;但即便如此&#xff0c;也不得不面对&#xff0c;FTP应用存在着严重缺陷&#xff1a; 传输效…

【八十二】【算法分析与设计】2421. 好路径的数目,928. 尽量减少恶意软件的传播 II,并查集的应用,元素信息绑定下标一起排序,元素通过下标进行绑定

2421. 好路径的数目 给你一棵 n 个节点的树&#xff08;连通无向无环的图&#xff09;&#xff0c;节点编号从 0 到 n - 1 且恰好有 n - 1 条边。 给你一个长度为 n 下标从 0 开始的整数数组 vals &#xff0c;分别表示每个节点的值。同时给你一个二维整数数组 edges &#xff…

Windows PC上从零开始部署ChatGML-6B-int4量化模型

引言 ChatGLM-6B是清华大学知识工程和数据挖掘小组&#xff08;Knowledge Engineering Group (KEG) & Data Mining at Tsinghua University&#xff09;发布的一个开源的对话机器人。6B表示这是ChatGLM模型的60亿参数的小规模版本&#xff0c;约60亿参数。 ChatGML-6B-in…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…