SpringBoot - 四种常见定时器

常见实现方案

  • @Scheduled注解:基于注解
  • Timer().schedule创建任务:基于封装类Timer
  • 线程:使用线程直接执行任务即可,可以与thread、线程池、ScheduleTask等配合使用
  • quartz配置定时器:基于springquartz框架


@Scheduled注解实现定时器

使用注解标记需要定时执行的方法,并设置执行时间,便可使其在指定的时间执行指定方法

步骤:

  • 使用注解@Scheduled标记目标方法,参数为执行时间
  • 使用注解@EnableScheduling标记目标方法所在的类,或者直接标记项目启动类

@Scheduled(fixedDelay = 5000):方法执行完成后等待5秒再次执行

@Scheduled(fixedRate = 5000):方法每隔5秒执行一次

@Scheduled(initialDelay=1000, fixedRate=5000):延迟1秒后执行第一次,之后每隔5秒执行一次

fixedDelayString、fixedRateString、initialDelayString:与上诉三种作用一直,但参数为字符串类型,因而可以使用占位符,形如@Scheduled(fixedDelayString = "${time.fixedDelay}")

@Scheduled(cron = "0 0,30 0,8 ? * ? "):方法在每天的8点30分0秒执行,参数为字符串类型,那么同理也可使用占位符

cron 该参数接收一个cron表达式cron表达式是一个字符串,字符串以5或6个空格隔开,分开共6或7个域,[年]不是必须的域,可以省略[年],则一共6个域

[秒] [分] [小时] [日] [月] [周] [年]
序号说明必填允许填写的值允许的通配符
10-59, - * /
20-59, - * /
30-23, - * /
41-31, - * ? / L W
51-12 / JAN-DEC, - * /
61-7 or SUN-SAT, - * ? / L #
71970-2099, - * /

通配符说明:

  • * 表示所有值。 例如:在分的字段上设置 *,表示每一分钟都会触发。
  • ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的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相同。

示例

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

每月最后一天23点执行一次:0 0 23 L * ?

每周星期六凌晨1点实行一次:0 0 1 ? * L

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

使用占位符

另外,cron属性接收的cron表达式支持占位符

配置文件:

time:cron: */5 * * * * *interval: 5

每5秒执行一次:

    @Scheduled(cron="${time.cron}")void testPlaceholder1() {System.out.println("Execute at " + System.currentTimeMillis());}@Scheduled(cron="*/${time.interval} * * * * *")void testPlaceholder2() {System.out.println("Execute at " + System.currentTimeMillis());}

第一次等待10秒,之后每3秒一次

@Component
@EnableScheduling
public class ScheduleTest {private int count = 0;/*** 第一次等待10秒,之后每3秒钟执行一次*/@Scheduled(initialDelay = 10000, fixedRate = 3000)public void test1() {System.out.println(count + ":" + (new Date()).toString());count++;}}

 


Timer().schedule实现定时器

核心包括Timer和TimerTask,均为jkd自带的工具类

TimerTask实际上就是一个Runnable而已,继承Runnable并添加了几个自定义的参数和方法

Timer字面意思即定时器,为jkd自带的工具类,提供定时执行任务的相关功能

实际上包括三个类:

Timer:即定时器主类,负责管理所有的定时任务,每个Timer拥有一个私有的TaskQueue和TimerThread,

TaskQueue:即任务队列,Timer生产任务,然后推到TaskQueue里存放,等待处理,被处理掉的任务即被移除掉

TaskQueue实质上只有一个长度为128的数组用于存储TimerTask、一个int型变量size表示队列长度、以及对这两个数据的增删改查

TimerThread:即定时器线程,线程会共享TaskQueue里面的数据,TimerThread会对TaskQueue里的任务进行消耗

TimerThread实际上就是一个Thread线程,会不停的监听TaskQueue,如果队列里面有任务,那么就执行第一个,并将其删除(先删除再执行)

流程分析

Timer生产任务(实际上是从外部接收到任务),并将任务推到TaskQueue里面存放,并唤醒TaskQueue线程(queue.notify())
TimerThread监听TaskQueue,若里面有任务则将其执行并移除队里,若没有任务则让队列等待(queue.wait())

构造

public Timer(String name, boolean isDaemon)
  • name:即线程名,用于区分不同的线程,缺省的时候默认使用"Timer-" + serialNumber()生成唯一线程名
  • isDaemon:是否是守护线程,缺省的时候默认为否

方法

schedule(TimerTask task, long delay):指定任务task,在delay毫秒延迟后执行


schedule(TimerTask task, Date time):指定任务task,在time时间点执行一次


schedule(TimerTask task, long delay, long period):指定任务task,延迟delay毫秒后执行第一次,并在之后每隔period毫秒执行一次


schedule(TimerTask task, Date firstTime, long period):指定任务task,在firstTime的时候执行第一次,之后每隔period毫秒执行一次


scheduleAtFixedRate(TimerTask task, long delay, long period):作用与schedule一致
scheduleAtFixedRate(TimerTask task, Date firstTime, long period):作用与schedule一致

实际上最后都会使用sched(TimerTask task, long time, long period),即指定任务task,在time执行第一次,之后每隔period毫秒执行一次

schedule使用系统时间计算下一次,即System.currentTimeMillis()+period

而scheduleAtFixedRate使用本次预计时间计算下一次,即time + period

对于耗时任务,两者区别较大,请按需求选择,瞬时任务无区别

取消任务方法:cancel(),会将任务队列清空,并堵塞线程,且不再能够接受任务(接受时报错),并不会销毁本身的实例和其内部的线程

净化方法:purge(),净化会将队列里所有被取消的任务移除,对剩余任务进行堆排序,并返回移除任务的数量

@Component
public class TimerTest {private Integer count = 0;public TimerTest() {testTimer();}public void testTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {try {//do SomethingSystem.out.println(new Date().toString() + ": " + count);count++;} catch (Exception e) {e.printStackTrace();}}}, 0, 1000);}
}


线程实现定时器

使用thread + runnable

public class ThreadTest {private Integer count = 0;public ThreadTest() {test1();}public void test1() {new Thread(() -> {while (count < 10) {System.out.println(new Date().toString() + ": " + count);count++;try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

使用线程池 + runnable

public class ThreadTest {private static final ExecutorService threadPool = Executors.newFixedThreadPool(5);// 线程池private Integer count = 0;public ThreadTest() {test2();}public void test2() {threadPool.execute(() -> {while (count < 10) {System.out.println(new Date().toString() + ": " + count);count++;try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}});}
}

使用org.springframework.scheduling.TaskScheduler+ runnable

设置触发频率为3000毫秒

@Component
public class ThreadTest {private Integer count = 0;private final TaskScheduler taskScheduler;public ThreadTest(TaskScheduler taskScheduler) {this.taskScheduler = taskScheduler;test3();}public void test3() {taskScheduler.scheduleAtFixedRate(() -> {System.out.println(new Date().toString() + ": " + count);count++;}, 3000);}
}

设置触发时间为每天凌晨1点

@Component
public class ThreadTest {private Integer count = 0;private final TaskScheduler taskScheduler;public ThreadTest(TaskScheduler taskScheduler) {this.taskScheduler = taskScheduler;test4();}public void test4() {taskScheduler.schedule(() -> {System.out.println(new Date().toString() + ": " + count);count++;}, new CronTrigger("0 0 1 * * ?"));}
}

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

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

相关文章

golang学习笔记——编写最简单的命令行工具

编写最简单的命令行工具 用户输入bufio 使用go语言编写最简单的命令行工具 mkdir hello-cli-demo cd hello-cli-demo # 查看环境变量 go envgo mod初始化 go mod init gitcode.com/m打开vscode&#xff0c;创建main.go package mainimport ("fmt""bufio&qu…

RK3568 CIF和ISP的关联

1. 引言 在本文档中&#xff0c;我们将介绍RK3568芯片的CIF&#xff08;Camera Interface&#xff09;和ISP&#xff08;Image Signal Processor&#xff09;模块。这两个模块是RK3568芯片的关键组成部分&#xff0c;用于图像采集和处理。 CIF是一个标准接口&#xff0c;用于…

快速测试 3节点的redis sentinel集群宕机2个节点以后是否仍能正常使用

有同事问我&#xff0c;三个redis sentinel节点&#xff0c;宕机两个节点以后&#xff0c;是否还能够正常的通过redis sentinel正常访问redis的数据。我想了想&#xff0c;理论上是可以的&#xff0c;但是我没试过&#xff0c;今天有时间就测试了一下。搭建环境和测试代码的过程…

Java并发(十七)----变量的线程安全分析

1、成员变量和静态变量是否线程安全 如果它们没有共享&#xff0c;则线程安全 如果它们被共享了&#xff0c;根据它们的状态是否能够改变&#xff0c;又分两种情况 如果只有读操作&#xff0c;则线程安全 如果有读写操作&#xff0c;则这段代码是临界区&#xff0c;需要考虑线…

深入了解Python pydash库

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在数据处理和分析领域&#xff0c;Python一直是一种强大的编程语言。然而&#xff0c;在处理大规模数据集和执行复杂操作时&#xff0c;有时候需要更高效的工具。在本文中&#xff0c;我们将深入探讨pydash库&am…

语义分割 简介及数据集简介

参考文章 MS COCO数据集介绍以及pycocotools简单使用-CSDN博客

[MySQL--进阶篇]存储引擎的体系结构、简介、特点、选择

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;不要在乎别人怎么看你&#xff0c;因为他们根本就没有时间&#xff0c;他们只关心他们自己。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 MySQL数据库 存储引擎 前言MySQL体…

代码随想录算法训练营第四十一天|343. 整数拆分、96.不同的二叉搜索树

代码随想录算法训练营第四十一天|343. 整数拆分、96.不同的二叉搜索树 整数拆分 343. 整数拆分 文章讲解&#xff1a;https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html 题目链接&#xff1a;https://leetcode.cn/problems/integer-break/ 视频讲解…

李宏毅gpt个人记录

参考&#xff1a; 李宏毅机器学习--self-supervised&#xff1a;BERT、GPT、Auto-encoder-CSDN博客 用无标注资料的任务训练完模型以后&#xff0c;它本身没有什么用&#xff0c;GPT 1只能够把一句话补完&#xff0c;可以把 Self-Supervised Learning 的 Model做微微的调整&am…

32.768KHz时钟RTC晶振精度PPM值及频差计算

一个数字电路就像一所城市的交通&#xff0c;晶振的作用就是十字路口的信号灯&#xff0c;因此晶振的品质及其电路应用尤其关键。数字电路又像生命体&#xff0c;它的运行就像人身体里的血液流通&#xff0c;它不是由单一的某个器件或器件单元构成&#xff0c;而是由多个器件及…

【Spring Boot 源码学习】ApplicationListener 详解

Spring Boot 源码学习系列 ApplicationListener 详解 引言往期内容主要内容1. 初识 ApplicationListener2. 加载 ApplicationListener3. 响应应用程序事件 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们从 Spring Boot 的启动类 SpringApplication 上入手&am…

如何查询川菜食材配料的API接口

在当今的美食文化中&#xff0c;菜谱不只是一张简单的食谱&#xff0c;更是了解美食文化和饮食知识的重要途径。然而&#xff0c;若没有准确的食材配料&#xff0c;烹制出的每道菜品都将难以达到完美的味道。因此&#xff0c;为了更好地满足人们对于菜谱和食谱的需求&#xff0…

C语言习题集(026)

//写一个函数&#xff0c;输入一个4位数字&#xff0c;要求输出这4个 //数字字符&#xff0c;但每两个数字间空一个空格。如输入 //1990&#xff0c;应输出"1 9 9 0"。 /* */ //解答&#xff1a; #include<stdio.h> void change(int a) { if(a/10!0) { chang…

linux权限管理以及shell

1.shell 1.1什么是shell? shell即外壳&#xff0c;是运行在linux系统上的一个脚本语言&#xff0c;包裹在linux内核的外面。我们常说的linux操作系统实际上是linux内核。我们使用的所有指令都是一个个程序&#xff0c;而shell指令就是一个将我们用户的操作翻译给linux内核的程…

软件设计之组合模式

组合模式&#xff1a;将对象组合成树形结构。 案例&#xff1a;公司管理。一个公司可以分总公司和分公司&#xff0c;无论是总公司还是分公司都有自己的部门&#xff0c;如人力资源管理部门、财务部门。分公司可以建立自己在不同地域的办事处。请使用组合模式打印出某个公司的…

SpringSecurity6 | 登陆后的跳转

SpringSecurity6 | 自定义认证规则 ✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Ja…

第九天:信息打点-CDN绕过篇amp;漏洞回链amp;接口探针amp;全网扫描amp;反向邮件

信息打点-CDN绕过篇 cdn绕过文章&#xff1a;https://www.cnblogs.com/qiudabai/p/9763739.html 一、CDN-知识点 1、常见访问过程 1、没有CDN情况下传统访问&#xff1a;用户访问域名-解析服务器IP–>访问目标主机 2.普通CDN&#xff1a;用户访问域名–>CDN节点–>…

面向LLM的App架构——业务维度

这是两篇面向LLM的大前端架构的第一篇&#xff0c;主要写我对LLM业务的认知以及由此推演出的大前端架构。由于我是客户端出身&#xff0c;所以主要以客户端角度来描述&#xff0c;并不影响对前端的适用性。 对LLM的认知 基于Google对AGI的论文&#xff0c;AGI或者LLM一定会朝…

浅谈ClickHouse性能监控与调优

ClickHouse性能监控与调优 ClickHouse是一个高性能的列式数据库管理系统&#xff0c;适用于实时分析和大数据处理。本文将详细讲解如何监控ClickHouse的性能指标、日志和查询统计信息&#xff0c;以及如何进行故障排查和性能调优。 一、监控性能指标 1. 系统表 ClickHouse提…

网络层重点协议——IP协议详解

✏️✏️✏️今天给大家分享的是网络层的重点协议——IP协议。 清风的CSDN博客 &#x1f6e9;️&#x1f6e9;️&#x1f6e9;️希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff01; ✈️✈️✈️动动你们发财的…