使用ScheduledExecutorService线程池手动动态控制定时任务
背景
在日常开发过程中,使用定时任务去执行一些业务逻辑是很常见的一种场景。比如定时发送短信,邮件,电商系统的定时自动收货、定时上下架功能等等。
一般实现定时任务有以下几种方案:
JDK自带
- JDK自带的Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,一般用的较少。
- JDK1.5+ 新增的ScheduledExecutorService:是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。
第三方框架
使用 Quartz、elastic-job、xxl-job 等开源第三方定时任务框架,适合分布式项目应用。不过配置起来稍显复杂,不太易上手。
Spring Task
Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。使用 Spring 提供的一个注解 @Schedule即可,开发简单,使用比较方便。
本文主要向大家介绍使用ScheduledExecutorService操作定时任务。
使用
1.启动类添加 @EnableScheduling 注解
2.创建定时任务类
ScheduleController.java
/** * <p> * 接口控制定时任务开始和停止 * </p> * * @className ScheduleController * @author Sue * @date 2021/7/28 **/ @RestController @RequestMapping("/task") public class ScheduleController { ScheduleTaskService scheduleTaskService; public ScheduleController(ScheduleTaskService scheduleTaskService) { this.scheduleTaskService = scheduleTaskService; } @PostMapping("/startCron") public R startCron() { scheduleTaskService.startCorn(); return R.ok("定时任务启动成功!"); } @PostMapping("/stopCron") public R stopCron() { scheduleTaskService.stopCorn(); return R.ok("定时任务关闭成功!"); } }
ScheduleTaskService.java
/** * <p> * 定时任务 * </p> * * @className ThreadPoolTaskSchedulerService * @author Sue * @create 2021/7/21 **/ public interface ScheduleTaskService { /** * <p> * 开始定时任务 * </p> * * @return boolean * @author Sue * @date 2021/7/21 */ boolean startCorn(); /** * <p> * 关闭定时任务 * </p> * * @return boolean * @author Sue * @date 2021/7/21 */ boolean stopCorn(); }
ScheduleTaskServiceImpl.java
/** * <p> * 定时任务 * </p> * * @className ThreadPoolTaskSchedulerServiceImpl * @author Sue * @create 2021/7/21 **/ @Slf4j @Service public class ScheduleTaskServiceImpl implements ScheduleTaskService { private ScheduledFuture<?> future; ThreadPoolTaskScheduler threadPoolTaskScheduler; public ScheduleTaskServiceImpl(ThreadPoolTaskScheduler threadPoolTaskScheduler) { this.threadPoolTaskScheduler = threadPoolTaskScheduler; } @Override public boolean startCorn() { if (future != null) { future.cancel(true); log.info("定时任务已停止"); } //每10秒执行一次 String cornConfig = "0/10 * * * * *"; future = threadPoolTaskScheduler.schedule(new ScheduledTaskRunnable(), new CronTrigger(cornConfig)); log.info("定时任务开启"); return false; } @Override public boolean stopCorn() { if (future != null) { future.cancel(true); log.info("定时任务已停止"); } return false; } static class ScheduledTaskRunnable implements Runnable { @Override public void run() { //需要执行的业务逻辑 log.info("定时任务开始执行,每10秒执行一次,当前时间{}", LocalDateTimeUtil.format(LocalDateTimeUtil.now(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } } }
3.项目启动后,调用接口,控制定时任务的启动和停止
测试
调用startCorn接口,可以看出定时任务成功开启
调用stopCorn接口,停止定时任务
补充
corn表达式的使用
cron 表达式是一个字符串,该字符串由 6 个空格分为 7 个域,每一个域代表一个时间含义。 通常定义 “年” 的部分可以省略,实际常用的 Cron 表达式由前 6 部分组成。格式如下:
[秒] [分] [时] [日] [月] [周] [年]
Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (optional field)
需要说明的是,Cron 表达式中,“周” 是从周日开始计算的。“周” 域上的 1 表示的是周日,7 表示周六。
通配符说明
- * 表示所有值. 例如:在分的字段上设置 “*”,表示每一分钟都会触发
- ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
- - 表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。
- , 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发
- / 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置’1/3’所示每月1号开始,每隔三天触发一次。
- L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
- W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,“W"前只能设置具体的数字,不允许区间”-").
- # 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;
提示:
'L’和 'W’可以组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发;
周字段的设置,若使用英文字母是不区分大小写的,即MON 与mon相同;
例子
- "0 */1 * * * ?" 每隔 1 分钟执行一次
- "0 24,30 * * * ?" 在24分,30分执行一次
- "0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点
- "0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时
- "0 0 12 ? * WED" 表示每个星期三中午12点
- "0 0 12 * * ?" 每天中午12点触发
- "0 15 10 ? * *" 每天上午10:15触发
- "0 15 10 * * ?" 每天上午10:15触发
- "0 15 10 * * ? *" 每天上午10:15触发
- "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
- "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
- "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
- "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
- "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
- "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
- "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
- "0 15 10 15 * ?" 每月15日上午10:15触发
- "0 15 10 L * ?" 每月最后一日的上午10:15触发
- "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
- "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
- "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
原文地址:https://www.cnblogs.com/sueyyyy/p/15069735.html
- [程序设计语言]-[核心概念]-03:控制流
- 简单代码实现“网站维护中”“coming soon” 效果
- 使用Topshelf创建Windows 服务
- 自定义AuthorizeAttribute
- 系统进程管理工具Process Explorer
- jquery 操作DOM元素(1)
- 开源的读取Excel文件组件-ExcelDataReader
- BlackPearl 的 ServiceObject 开发部署
- [程序设计语言]-[核心概念]-04:数据类型
- jquery 筛选元素(1)
- [程序设计语言]-00:目录
- 使用 SQL Server 2008 数据类型-xml 字段类型参数进行数据的批量选取或删除数据
- jquery 筛选元素 (2)
- 专家:中国还不是网络强国 今后须打破国外垄断
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Go 每日一库之 negroni
- Go 每日一库之 cli
- Go 每日一库之 cron
- Go 每日一库之 mapstructure
- Go 每日一库之 jobrunner
- 在本地运行 fyne 官网
- Python-科学计算-pandas-12-df单列计算
- JDK15就要来了,你却还不知道JDK8的新特性!
- 八佰:用Python看知乎 vs 豆瓣的战斗
- 学习Python你必须了解的lenna小姐姐
- 10行python代码制作笑死人不偿命的倒放gif
- “Hello Node.js” 这一次是你没见过的写法
- 作为DBA,你不得不掌握的压测工具
- Mac之vim普通命令使用
- selenium库的基本使用