Java的定时器Timer和定时任务TimerTask应用以及原理简析

article/2025/8/30 10:14:36

记录:272

场景:Java JDK自带的定时器Timer和定时任务TimerTask应用以及原理简析。在JDK工具包:java.util中可以找到源码,即java.util.Timer和java.util.TimerTask。TimerTask实现Runnable接口的run方法。Timer的属性TimerThread thread继承Thread。因此,Timer天生就具备多线程属性。这个轻量级的定时器和定时任务,在多线程的场景中使用极其便利和灵活。

版本

JDK 1.8
Spring Boot 2.6.3
Spring Framework 5.3.15

1.基础

Timer和TimerTask成对出现,Timer是定时器,TimerTask是定时任务。换句话说,定时任务TimerTask是给定时器Timer执行的具体任务。在JDK 1.8中TimerTask是抽象类(abstract),使用者继承TimerTask,并实现抽象方法run()方法即可。TimerTask实现Runnable接口的run()。

1.1 TimerTask

TimerTask,即java.util.TimerTask,一个抽象类(abstract修饰的类),实现(implements)可被线程执行的Runnable接口。

TimerTask需关注的内容。

(1)属性:final Object lock,多线程同步时使用。在Timer的TimerThread中同步锁synchronized会使用。

(2)属性:int state,标识TimerTask的当前状态。包括VIRGIN,SCHEDULED,EXECUTED,CANCELLED。

(3)属性:long nextExecutionTime,下一次执行的时间。

(4)属性:long period,重复任务的执行频率。

(5)方法:boolean cancel(),取消任务,实际标记任务状态state为CANCELLED。

(6)方法:scheduledExecutionTime(),获取调度时间。

(7)方法:protected TimerTask(),无参构造方法。

(8)方法:abstract void run(),线程执行具体函数,使用者的具体业务任务就在这个方法中写。

1.2 TaskQueue

TaskQueue,即java.util.TaskQueue。TaskQueue维护一个属性TimerTask[] queue,这是Timer的任务线程操作的底层队列。在Timer中有TaskQueue属性,是给任务线程TimerThread使用的。

TaskQueue需关注的内容。

(1)属性:TimerTask[] queue,队列中存放TimerTask。Timer的TimerThread通过遍历queue,取出TimerTask,根据TimerTask属性来确定当前是否执行。。

注意:使用者不需要直接操作TimerTask[] queue,它是交给Timer的TimerThread操作。

1.3 TimerThread

TimerThread,即java.util.TimerThread。继承Thread类。因此,天生就具备多线程属性。

TimerThread需关注的内容。

(1)属性:TaskQueue queue,TimerThread操作queue来找到当前需要执行的定时任务TimerTask。根据执行策略执行定时任务TimerTask中的run方法。

(2)方法:void run(),是TimerThread从Thread中继承的方法。线程执行的具体内容,必须在这个方法中写入或者在这个方法中被调用。TimerThread实现细节方法是void mainLoop()。在run方法中会调用mainLoop(),这样定时任务就被任务线程调用到。

(3)方法:void mainLoop(),TimerThread中逻辑细节落地方法。本方法内容在while (true)中,只要队列有任务就会无限循环。方法核心内容就是遍历TimerThread的属性:TaskQueue queue,找到符合条件的需执行的TimerTask,并执行TimerTask的run方法。

(4)方法: void start(),此方法是TimerThread从Thread继承来的,作用就是启动线程。TimerThread的start()方在Timer创建时就会调用。Timer创建后,TimerThread就启动了。

1.4 Timer

Timer,即java.util.Timer。使用者操定时器就是操作这个类的任务调度方法。

Timer的需关注的内容。

(1)属性:final TaskQueue queue,Timer存放所有的定时任务的队列,定时器Timer的核心就是遍历这个队列,找到当前时间下,符合条件的需执行的定时任务TimerTask。

(2)属性:final TimerThread thread,Timer执行定时任务的后台线程。定时任务具体执行逻辑在TimerThread的run方法中。TimerThread 线程在Timer的构造函数中会调用TimerThread的start()启动线程。

(3)构造函数

1>Timer(),无参构造函数。

2>Timer(boolean isDaemon),有参构造函数。设置定时器线程是否为守护线程。就是设置属性:TimerThread thread,调用Thread的setDaemon()方法。

3>Timer(String name),有参构造函数。设置线程名称。

4>Timer(String name, boolean isDaemon)。有参构造函数。设置线程名称和设置是否为守护线程。

(4)任务调度函数

1>任务调度函数

操作Timer定时器,实际就是操作如下任务调度函数。

public void schedule(TimerTask task, long delay);
public void schedule(TimerTask task, Date time);
public void schedule(TimerTask task, long delay, long period);
public void schedule(TimerTask task, Date firstTime, long period); 
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period);

2>任务调度函数(sched)

任务调度函数都会调用一个私有方法sched。

private void sched(TimerTask task, long time, long period);

TimerTask task:继承抽象类TimerTask,重写abstract void run()方法,把具体业务任务写在这个方法里。

long time:调度任务的起始时间值。调度是从这个时间为计算起点。

long period:任务调度频率。

把定时任务TimerTask添加到队列,就是在这个方法中实现。

(5)方法:void cancel(),取消队列里所有任务。

(6)方法:void purge(),清除队列里所有任务。

2.Timer原理解析

Timer原理解析,来自JDK中java.util.Timer源码逻辑。如有疑惑,可直接翻阅源码,便一目了然。

2.1 添加定时任务

把TimerTask添加到Timer内部的TaskQueue中。

2.2.1 自定义实现TimerTask的 run()

自定义实现抽象类TimerTask的 run()方法,即任务需要执行的业务逻辑写在run方法中。

2.2.2 创建定时器Timer

创建定时器Timer,会做三件核心事情。

(1)创建任务队列:TaskQueue queue。

(2)创建定时器线程:TimerThread thread。

(3)启动定时器线程:执行TimerThread的start()方法,即后台循环扫描的定时器线程启动。

2.2.3 调用Timer任务调度函数

添加定时任务就在调用Timer任务调度方法中完成。

(1)设置定时器任务调度参数。

(2)把自定义任务传递给任务调度函数。

2.2.4 Timer任务调度函数核心逻辑

Timer暴露给使用者的所有任务调度函数,最终都会落地在一个私有方法,即

void sched(TimerTask task, long time, long period);

核心逻辑如下:

(1)同步锁:synchronized(queue),锁住队列。队列所有操作在此锁内完成。

(2)同步锁:synchronized(task.lock),锁住任务。

此锁内主要操作:设置TimerTask任务的执行时间,执行频率,任务状态。操作完成立即解锁。

(3)队列TaskQueue queue,把设置好的TimerTask添加到TaskQueue 中。

(4)解除同步锁:synchronized(queue)。

2.2 执行定时任务

执行定时任务,在Timer的定时器线程TimerThread thread中,即线程的run()方法中,最终落地方式是 mainLoop()。注意mainLoop()是在run()方法中调用,是逻辑集中写在mainLoop()中,增加代码易读性。

2.2.1 while (true)入口

mainLoop()入口是while (true),即使循环扫描。

2.2.2 同步锁:synchronized(queue)

同步锁:synchronized(queue),锁住队列,一个Timer共用一个队列,因此使用synchronized有效。

2.2.3 判断队列是否为空

使用 while (queue.isEmpty() && newTasksMayBeScheduled)判断队列是否为空,如果为空,则 queue.wait()等待,注意wait()是java.lang.Object的方法,因此,此时while在卡主状态,直到queue.notify()被调用,才会继续。

2.2.4 确认队列为空跳出循环

使用if (queue.isEmpty()),判断队列确定为空了,那么就break跳出循环,其实任务线程就优雅结束了。

2.2.5 取出一个任务TimerTask

取出一个任务:task = queue.getMin();

2.2.6 同步锁:synchronized(task.lock),操作任务

使用同步锁:synchronized(task.lock),锁住任务。判断任务是否可执行。

(1)判断任务状态。

(2)取当前系统时间:System.currentTimeMillis()。

(3)取出的任务执行时间: task.nextExecutionTime。

(4)判断当前系统时间和任务执行时间,来确定任务是否需要执行。

(5)使用任务状态标识taskFired=true任务需启动;否则,不启动。。

(6)任务操作完成,解除同步锁:synchronized(task.lock)。

2.2.7 解除同步锁:synchronized(queue)

2.2.8 执行任务:task.run()。

自定义的定时任务TimerTask,在此处就被执行。

3.案例

本例每隔60秒,获取一次UUID。

(1)实现TimerTask的run方法,把具体执行的业务任务写到run方法中。

(2)创建Timer对象,配置定时任务和传入TimerTask对象。

Timer提供多种任务调度策略,本例使用固定频率任务调度。

TimeMonitor,实现了InitializingBean接口,在afterPropertiesSet中初始化定时器。也就是TimeMonitor被容器初始化完成后,就会触发Timer定时器初始化,Timer定时器内部的任务线程会启动和任务队列会把使用者的定时任务添加到队列。Timer定时器就会生效运行。

@Slf4j
@Service
public class TimeMonitor implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {startTimerMonitor();}private void startTimerMonitor() {TimerTask timerTask = new TimerTask() {@Overridepublic void run() {log.info("定时任务执行开始.");String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();log.info("执行业务,获取序列号,UUID = " + uuid);log.info("定时任务执行完成.");}};// 定时器Timer timer = new Timer();// 延时时间long delayTime = 6000L;// 执行频率long period = 1000 * 60;timer.scheduleAtFixedRate(timerTask, delayTime, period);}
}

4.测试

根据输出日志查看定时调度情况。

 以上,感谢。

2022年6月11日


http://chatgpt.dhexx.cn/article/nFmDrVsW.shtml

相关文章

原码、补码、反码的转换

1.原码 (1)一个正数,他的原码就是它对应的二进制数字。 (2)一个负数,按照绝对值大小转换成的二进制数,然后最高位补1,就是负数的原码。 2.反码 (1)正数的反…

原码、补码、反码的关系

一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1、机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1. 比如,十进制中的数 3 &…

原码,反码,补码,阶码,移码

本文转载自本站大佬“不去上课”,原文链接https://blog.csdn.net/ruidianbaihuo/article/details/87875178 原码,反码,补码,阶码,移码是什么?有什么区别(讨论机器数的表示) 本文内容参考自王达老师的《深入理解计算机…

原码、反码、补码之间的转换和简单运算

一、正整数的原码、反码、补码完全一样,即符号位固定为0,数值位相同 二、负整数的符号位固定为1,由原码变为补码时,规则如下: 1、原码符号位1不变,整数的每一位二进制数位求反,得到反码 2、反码…

原码,反码,补码的概念

计算机里都是以补码的形式存储数据,电脑只能识别二进制的0和1, 一个字节(8位)为例 原码 :最高位符号位,0代表正数,1代表负数,非符号位为该数字绝对值的二进制。 反码:正…

C语言——原码, 反码, 补码 详解

目录 一. 机器数和真值 1、机器数 2、真值 二. 原码, 反码, 补码的基础概念和计算方法. 1. 原码 2. 反码 3. 补码 三. 为何要使用原码, 反码和补码 四 原码, 反码, 补码 再深入 同余的概念 负数取模 开始证明 一. 机器数和真值 在学习原码, 反码和补码之前, 需要先…

原码、反码、补码、移码的概念及转换

目录 前言 1.原码 2.反码 3.补码 4.移码 前言 学习完数的小数点表示,下一个需要解决的问题就是数的机器码表示问题。 在计算机中对数据进行运算操作时,符号位应该如何表示?是否也同数值位一道参加运算操作呢?如果参加&…

mt7620芯片处理器核心资料

MT7620产品系统整合了2T2R 802.11n Wi-Fi 收发器、580MHz MIPS 24KEc™ 中央处理器 (CPU)、5 端口高速以太网络端口物理层 (Ethernet PHY)、HNAT、存储器加速器、USB2.0 主机/设备,以及多种慢速输入输出 (U客论坛)。MT7620A 支持 PCIe、RGMII,适用于 AC7…

mt7620参考设计原理图,mt7620芯片资料,mt7620处理器资料

mt7620参考设计原理图,芯片资料,处理器资料 核心资料 芯片处理器资料 设计注意事项 处理器大全 音频语音设计资料文档(U客论坛) MT7620产品系统整合了2T2R 802.11n Wi-Fi 收发器、580MHz MIPS 24KEc™ 中央处理器 (CPU)、5 端口高速以太网络端口物理层 (Ethernet PHY)、HNAT…

MT7682参考手册,MT7682芯片资料介绍

MT7682 Reference Manual 2.总线体系结构与内存映射 MediaTek MT7682采用32位多AHB矩阵,为物联网和可穿戴设备提供低功耗、快速、灵活的数据操作.表2.1-1显示了总线主机之间的互连(Cortex-M4,四个spi主站,spi从机,调试系统&#…

MT7628 openwrt学习(1)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、拿到板子之后干的事情二、openwrt编译文件下载tftp中的坑 最后是SSH和简单的编译 前言 主要是用来记录我的MT7628踩坑经历,所有东西都是开源的…

mt7621芯片更换ram

最近公司设备的内存占用率过高,经常性的导致设备挂掉,于是准备换一个ram。 解决方案: 在openwrt目录下,进入target/linux/ramips/dts目录,找到设备使用的CPU型号对应的dts文件,然后修改其中内容。将memory的…

MT7621处理器资料解析,MT7621数据表

MT7621处理器资料解析,MT7621数据表 MT7621 Wi-Fi 系统单芯片包含功能强大的 880 MHz MIPS 1004KEc™ 双核心中心处理器 (CPU)、5 端口 Gigabit 以太网络交换器,以及RGMII、PCIe、USB、SD-XC 等众多连接选项。这款全新系统单芯片亦随附我们经现场验证的硬件支持,涵盖网络地…

MT6261处理器参数介绍,MT6261芯片资料

MT6261处理器: MT6261是一种基于低功耗CMOS工艺的集成前沿电源管理单元、模拟基带和无线电电路的单片芯片。 MT6261是一种功能丰富、功能非常强大的用于高端GSM/GPRS能力的单芯片解决方案。基于32位ARM7EJ-S TM RISC处理器,MT6261的卓越处理能力TH高带…

MT8167处理器型号对比,MT8167芯片资料介绍

MT8167平台有两个版本,分别是MT8167A和MT8167B。两者之间最大的、唯一的区别在于MT8167A提供略高的处理和图形性能规格,最大的区别是支持60pps的1080p视频解码(MT8167B的30fps以上)和全高清支持(19201200) …

mt7682芯片处理器详细资料介绍

MTK MT7682S是基于一个高度集成的芯片组,包括一个微控制器单元(MCU)、一个低功耗的1x11n单波段Wi-Fi子系统和一个电源管理单元(PMU)。单片机是一个带有浮点单元的ARM Cortex-M4处理器,与1MB的闪存集成在一起。 Wi-Fi子系统包含802.11b/g/n无线电、基带和…

MT7621A的首板终于收到了

经过一个近一个月的生产加工,MT7621A的硬件终天收到了。 先上两个图: 正面 刚拿到电路板,测试电源时竞然发现1.1VD对地短路。(一想这个可完了,刚做的电路板就短路,一定死定了) 通过认真仔细的分…

MT7628 wifi模块,MTK路由器芯片介绍

MT7628处理器: MT7628nn/mt7628an 系列产品是新一代2T2R 802.11n Wi-Fi AP / 路由器 (系统单芯片)。 MT7628可提升射频效能表现、减低功耗,并将整体物料清单 (BOM) 成本优化,令它成为性价比最出众的 2x2 11n 解决方案。MT7628产品家族整合了…

MT7621路由器芯片/处理器参数介绍

MT7621路由器芯片包括一个880 MHz MIPS 1004Kc™CPU双核,一个5端口10/100/1000交换机/PHY和一个RGMII。嵌入式高性能cpu可以很容易地处理高级应用程序。如路由、安全和VoIP等。MT7621还包括支持多种应用程序的接口选择,例如用于访问外部存储的USB端口。 …

MT7621_基础篇(1) 芯片资料了解 一

项目开发需要使用MT7621,负责驱动移植,适配公司板卡。网上相关资料多为涉及openwrt的,不太符合我们裁剪要求,故记录此流程,进行深入学习研究。首先先对芯片资源做一个了解。后续计划包括uboot移植分析,kern…