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

在这里插入图片描述

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.hqwc.cn/news/318442.html

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

相关文章

阿里云性能测评ESSD Entry云盘、SSD云盘、ESSD和高效云盘

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

语音AI小夜灯项目

一、项目简介 使用ESP32-S3N8R8模块作为主控芯片&#xff0c;S3内核增加了用于加速神经网络计算和信号处理等的指令&#xff0c;这使得我们可以使用它来快速解析训练好的语音模型进行语音识别的功能。 二、原理解析 本项目由四个部分组成&#xff0c;电源部分、LED照明部分、…

作业--day41

利用模板类完成顺序表 #include <iostream>using namespace std;//模板类 template <typename T> class SeqTab{T arr[20];int maxsize; public:SeqTab():maxsize(0){}void Insert(T a);void Search(T a);void Delete(int index);void Show(); };//尾插 template …

《合成孔径雷达成像算法与实现》Figure5.13

clc clear close all距离向参数 R_eta_c_1 450; % 景中心斜距 R_eta_c_2 850; % 景中心斜距 R_eta_c_3 1250; % 景中心斜距方位向参数 c 3e8; f0 5.3e6; Vr 7100; Ta 0.64; % 目标照射时间 Ka 2095; % 方位向调频率 theta_r_c 0; % 斜视角参数计算…

C语言实例_math.h库函数功能及其用法详解

一、前言 数学在计算机编程中扮演着至关重要的角色&#xff0c;C语言的math.h头文件提供了一系列的函数和工具&#xff0c;用于数学计算和常用数学函数的实现。这些函数包括数值运算、三角函数、指数对数函数等&#xff0c;为开发人员提供了强大的数学处理能力。本文将对math.…

C++-类和对象(2)

1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下 6 个默认成员 函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译…

layui 日期不能重新渲染

需求&#xff1a;当用户选择开始日期的时候&#xff0c;结束日期要小于开始日期。 遇到的问题&#xff1a; 当切换开始时间时&#xff0c;结束时间的输入框不会被重新渲染。 解决&#xff1a; html代码&#xff1a; <div class"layui-form-item"><label…

引导和服务

一、Linux操作系统引导过程 1、引导过程总览图 2、引导过程的详细步骤 1.开机自检&#xff08;BIOS&#xff09; 服务器主机开机以后&#xff0c;将根据主板BIOS中的设置对CPU、内存、显卡、键盘等设备进行初步检测&#xff0c;检测成功后根据预设的启动顺序移交系统控制权&a…

VitulBox中Ubuntu虚拟机安装JAVA环境——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项

前言 在进行之后操作是请下载好JDK&#xff0c;之后的内容是以Ubuntu虚拟机中安装java环境续写。 建议大家先把文章看完在配&#xff0c;因为有一些出错原有在后面&#xff0c;看完之后调整顺序。 提示&#xff1a;以下操作是在虚拟机hadoop用户下操作的&#xff0c;并为安装…

C++ 实现Windows WIFI管理器

文章目录 前言一、代码二、补充知识三、遇到的问题字符集转换 四、剩余问题总结 前言 出于项目需要&#xff0c;需要用C开发一个wifi界面&#xff0c;实现wifi扫描、wifi连接与断开、wifi密码记住的基础功能。 一、代码 话不多说&#xff0c;直接上代码。 #pragma once #inc…

基于猫群算法优化的Elman神经网络数据预测 - 附代码

基于猫群算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于猫群算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于猫群优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

SQL 在已有表中修改列名的方法

文章目录 1. MySQL2. SQL Server3. Oracle / PostgreSQL Question&#xff1a; 假设有一张表 StudentInfo&#xff0c;表中有一个列名是 Student_Name &#xff0c;想要把这个列名改成 StudentName 应该如何操作&#xff1f; 建表语句如下&#xff1a; --建表 if object_id(S…