【Java进阶篇】Java中Timer实现定时调度的原理(解析)

news2024/11/15 8:43:38

在这里插入图片描述

Java中Timer实现定时调度的原理

  • ✔️ 引言
  • ✔️JDK 中Timer类的定义
  • ✔️拓展知识仓
    • ✔️优缺点


✔️ 引言


Java中的Timer类是用于计划执行一项任务一次或重复固定延迟执行的简单工具。它使用一个名为TaskQueue的内部类来存储要执行的任务,这些任务被封装为TimerTask对象。

Timer实现定时调度的基本原理:

  1. 创建 Timer 对象:当你创建一个Timer对象时,它会实例化一个线程(不是守护线程),这个线程用于执行计划任务。
  2. 添加任务:你可以使用schedulescheduleAtFixedRate方法向Timer添加任务。这些方法接受一个TimerTask对象和一个延迟时间(以及可选的重复间隔)。
  3. 内部存储Timer内部使用一个优先级队列(具体是TaskQueue类)来存储要执行的任务。队列中的每个元素都是一个表示要执行的任务的TimerTask对象。队列根据任务的执行时间进行排序,以确保任务按照预定的时间顺序执行。
  4. 执行线程Timer类的线程在后台运行,并定期检查任务队列。当线程发现队列中有任务到达其预定的执行时间时,它会从队列中取出该任务并执行它。
  5. 处理重复任务:对于需要重复执行的任务,Timer会重新计算下一个执行时间,并将任务重新放入队列中。这样,当任务的下一次执行时间到达时,线程会再次从队列中取出并执行它。
  6. 取消任务:你可以使用Timer.cancel()方法来取消所有已调度的任务,或者使用TimerTask.cancel()方法来取消单个任务。取消的任务将从队列中移除,并且不会再次调度。
  7. 注意事项:虽然Timer在简单场景下可以很好地工作,但它并不是最适合所有场景的定时任务解决方案。特别是,在需要更复杂的调度需求或在并发环境中,使用ScheduledExecutorService可能是更好的选择。

总而言之,Java中的Timer类通过内部使用一个优先级队列来存储和管理定时任务,并通过一个单独的线程来检查和执行这些任务,从而实现了定时调度功能。


✔️JDK 中Timer类的定义


Java中的Timer类是一个定时调度器,用于在指定的时间点执行任务。JDK 中Timer类的定义如下:


public class Timer {
	
	/**
	*
	*     The timer task queue.This data structure is shared with the timer
	*     thread. The timer produces tasks, via its various schedule calls,
	*     and the timer thread consumes, executing timer tasks as appropriate,
	*     and removing them from the queue when they're obsolete.
	*/

	private final TaskOueue queue = new TaskOueue() ;

	/**
	*     The timer thread
	*/
	private final TimerThread thread = new TimerThread(queue);
}

以上就是 Timer 中最重要的两入成员变量:


1、TaskQueue: 一个任务队列,用于存储已计划的定时任务。任务队列按照任务的执行时间进行排序,确保最早执行的任务排在队列前面。在队列中的任务可能是一次性的,也可能是周期性的。


2、TimerThread : Timer 内部的后台线程,它负责扫描 TaskQueue 中的任务,检查任务的执行时间,然后在执行时间到达时执行任务的 run() 方法。TimerThread 是一个守护线程,因此当所有非守护线程完成时,它会随之终止。


任务的定时调度的核心代码就在TimerThread 中:


/**
*   @author xinbaobaba
*/

class TimerThread extends Thread {
	//标志位
	boolean newTasksMayBeScheduled = true;

	/**
	*    存储 TimerTask 的队列
	*/
	private TaskQueue queue ;
	
	TimerThread(TaskQueue queue) {
		this .queue = queue;
	}

	public void run() {
		try {
			mainLoop();
		} finally {
			synchronized (queue) {
				newTasksMayBeScheduled = false;
				queue.clear();
			}
		}
	}

	/**
	*    主要的计时器循环。
	*/

	private void mainLoop() {
		while (true) {
			try {
				TimerTask task;
				boolean taskFired;
				synchronized (queue) {
					//等待队列变为非空
					while (queue.isEmpty() && newTasksMayBeScheduled)
					queue .wait() ;
					if (queue.isEmpty())
						// 队列为空,将永远保持为空; 线程终止
						break;

					//队列非空;查看第一个事件并执行相应操作
					long currentTime, executionTime;
					task = queue .getMin();
					synchronized (task.lock) {
						if (task.state == TimerTask.CANCELLED) {
							queue .removeMin() ;
							//无需执行任何操作,再次轮询队列
							continue; 
						}
						currentTime = System.currentTimeMillis();
						executionTime = task.nextExecutionTime;
						if (taskFired = (executionTime <= currentTime)) {
							if (task.period == 0) {// 非重复,移除
								queue.removeMin);
								task.state = TimerTask.EXECUTED;
							} else {// 重复任务,重新安排
								queue.rescheduleMin(
									task.period <  ? currentTime   -   task.period : executionTime + task.period);
							}
						}
					}
					if (!taskFired)  // 任务尚未触发;等待
					queue .wait(executionTime - currentTime);
				}
				if (taskFired) // 任务触发;运行它,不持有锁
				task.run();
			} catch (InterruptedException e) {
			}
		}
	}
}

可以看到,TimerThread的实际是在运行mainLoop方法,这个方法一进来就是一个while(true)的循环,他在循环中不断地从TaskQueue中取出第一个任务,然后判断他是否到达执行时间了,如果到了,就触发任务执行。否则就继续等一会再次执行。


不断地重复这个动作,从队列中取出第一个任务进行判断,执行。。。


这样只要有新的任务加入队列,就在队列中按照时间排队,然后唤醒timerThread重新检查队列进行执行就可以了。代码如下:


private void sched(TimerTask task, long time, long period) {
	if (time < 0) {
		throw new IllegalArgumentException("Illegal execution time.");
	}
	// Constrain value of period sufficiently to prevent numeric
	// overflow while still being effectively infinitely large.
	if (Math.abs(period) > (Long.MAX VALUE >> 1))
		period >>= 1;


	synchronized(queue) {
		if (!thread.newTasksMayBeScheduled)
			throw new IllegalstateException("Timer already cancelled.");	
		
		synchronized(task.lock) {
			if (task.state != TimerTask.VIRGIN) 
				throw new IllegalstateException("Task already scheduled or cancelled");
			task.nextExecutionTime = time;
			task.period = period;
			
			task.state = TimerTask.SCHEDULED;
		}

		//新任务入队列
		queue.add(task);
		//唤醒任务
		if (queue.getMin() == task)
			queue .notify();
	}
}

✔️拓展知识仓


✔️优缺点


Timer 类用于实现定时任务,最大的好处就是他的实现非常简单,特别的轻量级,因为它是Java内置的,所以只需要简单调用就行了。


但是他并不是特别好的解决定时任务的方案,因为他存在以下问题:


1、Timer内部是单线程执行任务的,如果某个任务执行时间较长,会影响后续任务的执行


2、如果任务抛出未捕获异常,将导致整个 Timer 线程终止,影响其他任务的执行


3、Timer 无法提供高精度的定时任务。因为系统调度和任务执行时间的不确定性,可能导致任务执行的时间不准确


4、虽然可以使用 cancel 方法取消任务,但这仅仅是将任务标记为取消状态,仍然会在任务队列中占用位置,无法释放资源。这可能导致内存泄漏


5、当有大量任务时,Timer 的性能可能受到影响,因为它在每次扫描任务队列时都要进行时间比较


6、Timer执行任务完全基于JVM内存,一旦应用重启,那么队列中的任务就都没有了

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

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

相关文章

条款16:成对使用 new 和 delete 时要采用相同形式

下面程序的行为是未定义的。至少&#xff0c;stringArray指向的100个string对象中有99个不太可能被正确地析构。 被delete的指针指向单个对象还是一个对象数组&#xff1f;内存数组通常包括数组的大小&#xff0c;delete可以知道需要调用多少个析构函数。 使用delete时使用了方…

SpringBoot 接口对枚举类型的入参以及出参的转换处理

目录 1、在项目中使用枚举类型2、不做任何处理的演示效果2.1、接口出参2.2、接口入参 3、用枚举的code作为参数和返回值3.1 代码案例3.1.1、定义枚举基础接口BaseEnum&#xff0c;每个枚举都实现该接口3.1.2、性别Sex枚举并实现接口BaseEnum3.1.3、定义BaseEnum枚举接口序列化3…

前端工程化回顾-vite 构建神器

1.构建vite 项目 pnpm create vite2.常用的配置&#xff1a; 1.公共资源路径配置&#xff1a; base: ./, 默认是/2.路径别名配置&#xff1a; resolve: {alias: {: path.resolve(__dirname, ./src),ass: path.resolve(__dirname, ./src/assets),comp: path.resolve(__dirnam…

正则表达式 详解,10分钟学会

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论正则表达式。 正则表达式是一种用于匹配和操作文本的工具&#xff0c;常用于文本查找、文本替换、校验文本格式等场景。 正则表达式不仅是写代码时才会使用&#xff0c;在平常使用的很多文本编辑软件&#xff0c;都…

多模态大模型Vary:扩充视觉Vocabulary,实现更细粒度的视觉感知

前言 现代大型视觉语言模型(LVLMs)具有相同的视觉词汇- CLIP&#xff0c;它可以涵盖大多数常见的视觉任务。然而&#xff0c;对于一些需要密集和细粒度视觉感知的特殊视觉任务&#xff0c;例如文档级OCR或图表理解&#xff0c;特别是在非英语场景下&#xff0c;clip风格的词汇…

盛最多水的容器(力扣11题)

例题&#xff1a; 分析&#xff1a; 这道题给出了一个数组&#xff0c;数组里的元素可以看成每一个挡板&#xff0c;要找到哪两个挡板之间盛的水最多&#xff0c;返回盛水量的最大值。这其实是一个双指针问题。 我们可以先固定第一个挡板( i )和最后一个挡板( j )&#xff0c…

FreeRTOS——计数型信号量知识总结及实战

1计数型信号量概念 1&#xff09;计数型信号量相当于队列长度大于1 的队列&#xff0c;因此计数型信号量能够容纳多个资源 2&#xff09;适用场景&#xff1a; 事件计数&#xff1a; 当每次事件发生后&#xff0c;在事件处理函数中释放计数型信号量&#xff08;计数值1&#x…

mysql查询表里的重复数据方法:

1 2 3 4 INSERT INTO hk_test(username, passwd) VALUES (qmf1, qmf1),(qmf2, qmf11) delete from hk_test where usernameqmf1 and passwdqmf1 MySQL里查询表里的重复数据记录&#xff1a; 先查看重复的原始数据&#xff1a; 场景一&#xff1a;列出username字段有重读的数…

【算法每日一练]-dfs bfs(保姆级教程 篇8 )#01迷宫 #血色先锋队 #求先序排列 #取数游戏 #数的划分

目录 今日知识点&#xff1a; 使用并查集映射点&#xff0c;构造迷宫的连通块 vis计时数组要同步当回合的处理 递归求先序排列 基于不相邻的取数问题&#xff1a;dfs回溯 n个相同球放入k个相同盒子&#xff1a;dfs的优化分支暴力 01迷宫 血色先锋队 求先序排列 取数游…

[Android]RadioButton控件

RadioButton控件 RadioButton控件是单选按钮控件&#xff0c;它继承自Button控件&#xff0c;可以直接使用Button控件支持的各种属性和方法。 与普通按钮不同的是&#xff0c;RadioButton控件多了一个可以选中的功能&#xff0c;能额外指定一个android&#xff1a;checked属性…

谷歌Gemini模型,碾压GPT-4!

谷歌Gemini 1.0革新&#xff0c;推出Gemini Ultra、Gemini Pro和Gemini Nano模型。Gemini Ultra强大但慢&#xff0c;Gemini Pro通用&#xff0c;Gemini Nano高效。Gemini模型在多领域与ChatGPT竞争&#xff0c;尤其Gemini Pro已应用于Bard。Gemini模型预计将在2024年通过Bard …

c语言结构体学习

文章目录 前言一、结构体的声明1&#xff0c;什么叫结构体?2&#xff0c;结构体的类型3,结构体变量的创建和初始化4&#xff0c;结构体的类型5&#xff0c;结构体的初始化 二、结构体的访问1&#xff0c;结构体成员的点操作符访问2&#xff0c;结构体体成员的指针访问 三、结构…

【解决】Unity 设置跨设备分辨率表现

开发平台&#xff1a;Unity 2018版本以上 开发语言&#xff1a;CSharp 编程平台&#xff1a;Visual Studio 2022   问题描述 使用 UnityEngine.dll 中关于设置分辨率的方法时&#xff0c;无法满足应用以设定分辨率进行屏幕显示问题。因而造成画面不同程度的拉伸情况。而这种情…

【Java】接口和抽象类有什么共同点和区别?

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【Java】接口和抽象类有什么共同点和区别&…

工作流入门这篇就够了!

总概 定义&#xff1a;工作流是在计算机支持下业务流程的自动或半自动化&#xff0c;其通过对流程进行描述以及按一定规则执行以完成相应工作。 应用&#xff1a;随着计算机技术的发展以及工业生产、办公自动化等领域的需求不断提升&#xff0c;面向事务审批、材料提交、业务…

力扣hot100 对称二叉树 递归 队列

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f468;‍&#x1f3eb; 参考思路 递归的难点在于&#xff1a;找到可以递归的点 为什么很多人觉得递归一看就会&#xff0c;一写就废。 或者说是自己写无法写出来&#xff0c;关键就是你对递归理解的深不深。 对于此题&#xf…

day04 两两交换链表中的节点 删除链表的倒数第N个节点 链表相交 环形链表Ⅱ

题目1&#xff1a;24 两两交换链表中的节点 题目链接&#xff1a;24 两两交换链表中的节点 题意 两两交换链表中相邻的节点&#xff0c;返回交换后链表的头节点 虚拟头节点 注意终止条件&#xff0c;考虑节点的奇偶数&#xff0c;根据奇偶数确定终止条件 注意定义中间变量…

Amos各版本安装指南

Amos下载链接 https://pan.baidu.com/s/1uyblN8Q-knNKkqQVlNnXTw?pwd0531 1.鼠标右击【Amos28】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;选择【解压到 Amos28】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Amos28】选择【以管理员身份运行…

干洗店洗鞋店小程序核心功能有哪些?

在繁忙的生活中&#xff0c;我们的鞋子常常承载着风尘仆仆的故事。而洗鞋小程序&#xff0c;就是那个让您的鞋子焕然一新的魔法师。通过这个小程序&#xff0c;您可以在线预约、支付&#xff0c;查询洗鞋订单&#xff0c;并与洗鞋店铺进行互动&#xff0c;轻松享受专业的洗鞋服…

计算机毕业设计 基于SpringBoot的工作量统计系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…