Jave 定时任务:使用Timer类执行定时任务为何会发生任务阻塞?如何解决?
IDE:IntelliJ IDEA 2022.2.3 x64
操作系统:win10 x64 位 家庭版
JDK: 1.8
文章目录
- 一、Timer类是什么?
- 二、Timer类主要由哪些部分组成?
- 1.TaskQueue
- 2. TimerThread
- 三、示例代码分析
- 四、自定义TimerTask为什么会发生任务相互阻塞的问题?
- 4.1 使用schedule添加任务,如任务执行超时,会导致任务丢失(少执行)
- 4.2 使用scheduleAtFixedRate添加任务,如任务执行超时,会导致任务执行时间乱掉,下一个任务会马上执行
- 五、Timer类的应用特性
- 六、如何解决任务阻塞问题?
提示:以下是本篇文章正文内容,下面案例可供参考
一、Timer类是什么?
Java Timer类是一个用于调度任务的类,它可以在指定的时间间隔内执行一次或多次任务。它提供了一种简单的方式来安排和执行定时任务,可以用于各种应用程序中,如计划任务、定时器等。
Java Timer类位于java.util包中,它有两个主要的子类:Timer和TimerTask。其中,Timer类用于调度任务,而TimerTask类则表示一个具体的任务,需要实现run()方法来定义任务的具体行为。
使用Java Timer类可以方便地创建和管理定时任务,但需要注意的是,它的精度有限,如果需要更高精度的任务调度,可以考虑使用ScheduledThreadPoolExecutor等其他工具。
二、Timer类主要由哪些部分组成?
1.TaskQueue
官方解释
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.
一句话,就是一个用于存储和处理任务的队列,里面存放TimeTask
2. TimerThread
官方解释
The timer thread.
显而易见,就是处理线任务的线程
三、示例代码分析
示例代码如下所示
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class myTimerTest {public static void main(String[] args){Timer timer = new Timer(); //任务执行for (int i = 0; i < 2 ; i++) {TimerTask timerTask = new FooTimerTask("FooTimerTask"+i);//将timerTask以当前时间执行,以2秒时间为间隔再次触发,即12:00:00执行,那么12:00:02 会再次触发执行timer.schedule(timerTask,new Date(),2000);//任务添加}}
}class FooTimerTask extends TimerTask {private String name;public FooTimerTask(String name) {this.name = name;}@Overridepublic void run() {try {System.out.println(name+" - startTime = "+new Date());//延迟3秒执行Thread.sleep(3000);System.out.println(name+" - endTime = "+new Date());} catch (InterruptedException e) {e.printStackTrace();}}
}
注意
其中,任务会在 Timer timer = new Timer();时执行,而并非大家认为的在timer.schedule(timerTask,new Date(),2000);时执行,该代码是将该timerTask添加到TaskQueue中(任务队列中)
不信,请看如下的源代码
①在new Timer()时执行任务
②在timer.schedule(timerTask,new Date(),2000)时添加任务至任务队列中
通过上述源代码演示,Timer类是在new Timer()中以多线程的方式运行TimerThread的start()方法,进而调用其中的run()方法。而我们自子自定义的FooTimerTask 的run()方法却是以单线程的方式被调用。
在TimerThread中的run方法中mainLoop方法里以死循环不断检查是否有任务需要开始执行了,有就执行它,执行任务也是用这个线程执行。
何以见得?
在mainLoop方法中
我们自定义的FooTimerTask会以单线程的方式执行,这样任务可能会相互阻塞
四、自定义TimerTask为什么会发生任务相互阻塞的问题?
4.1 使用schedule添加任务,如任务执行超时,会导致任务丢失(少执行)
示例代码如下所示
public class myTimerTest {public static void main(String[] args){Timer timer = new Timer(); //任务执行for (int i = 0; i < 2 ; i++) {TimerTask timerTask = new FooTimerTask("FooTimerTask"+i);//将timerTas以当前时间执行,间隔2s后触发执行,比如在12:00:00执行timerTas,下一次触发就是12:00:02时执行,余者类推timer.schedule(timerTask,new Date(),2000);//任务添加}}
}class FooTimerTask extends TimerTask {private String name;public FooTimerTask(String name) {this.name = name;}@Overridepublic void run() {try {System.out.println(name+" - startTime = "+new Date());//延迟3秒执行Thread.sleep(3000);System.out.println(name+" - endTime = "+new Date());} catch (InterruptedException e) {e.printStackTrace();}}
}
示例运行如下
这里有个问题:根据上述代码定义,自定义timerTask会在间隔2s后执行,而timerTask自己的执行会延迟3s才会真正结束,那么我们所推测它的执行场景应该是这样的:
假如任务A在12:00:00开始执行,12:00:03执行结束,那么下一个任务B将会在12:00:05开始执行
但上述代码的运行结果却与我们的推测 大相径庭
思考①
那如果timerTask去掉延迟3s的代码,运行结果应该是如“任务A在12:00:00开始执行,12:00:0执行结束,那么下一个任务B将会在12:00:03开始执行”这样执行吧?
代码示例如下
class FooTimerTask extends TimerTask {private String name;public FooTimerTask(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name+" - startTime = "+new Date());System.out.println(name+" - endTime = "+new Date());
// try {
// System.out.println(name+" - startTime = "+new Date());
// //延迟3秒执行
// Thread.sleep(3000);
// System.out.println(name+" - endTime = "+new Date());
// } catch (InterruptedException e) {
// e.printStackTrace();
// }}
}
运行如下
看来,任务阻塞的问题就出现在延迟3s的代码上
思考②
如果一个任务本身执行时间过长,超过预设的时间间隔(即任务执行超时),为什么会发生任务阻塞的问题?
任务阻塞问题;会导致后面的任务往后推移,预想在这个间隔内存在的任务执行就没了,任务少执行了【假如在10s的时间段内,任务正常执行5次,假如发生任务超时,可能会执行3-4次】
通过如下追踪源代码可知
①追踪进入schedule()方法中
注意:schedule方法将period值加了负号,即-period
②追踪进入sched()方法中
任务task的nextExecutionTime被赋值为time(传入的时间)
③追踪进入run()方法中的mainLoop()方法中
④追踪进入queue.rescheduleMin()方法中
⑤追踪进入fixDown()方法中
结论
由此可知,schedule里Timertask真正的执行时间取决上一个任务的结束时间,并非以预设的时间为准,故如某一个任务执行超时,则有可能出现任务丢失的问题
4.2 使用scheduleAtFixedRate添加任务,如任务执行超时,会导致任务执行时间乱掉,下一个任务会马上执行
示例代码如下所示
public class myTimerTest {public static void main(String[] args){Timer timer = new Timer(); //任务执行for (int i = 0; i < 2 ; i++) {TimerTask timerTask = new FooTimerTask("FooTimerTask"+i);timer.scheduleAtFixedRate(timerTask,new Date(),2000);}}
}class FooTimerTask extends TimerTask {private String name;public FooTimerTask(String name) {this.name = name;}@Overridepublic void run() {try {System.out.println(name+" - startTime = "+new Date());//延迟3秒执行Thread.sleep(3000);System.out.println(name+" - endTime = "+new Date());} catch (InterruptedException e) {e.printStackTrace();}}
}
运行如下
思考
为什么一旦任务发生超时,下一个任务会马上触发执行?
通过如下追踪源代码可知
①追踪进入schedule()方法中
②追踪进入sched()方法中
任务task的nextExecutionTime被赋值为time(传入的时间)
③追踪进入run()方法中的mainLoop()方法中
注:剩余追踪步骤和4.1小节一致,故不予展示
结论
scheduLeAtFixedRate()方法会严格按照预设时间作为TimerTask的执行时间,如果发生任务超时,下一个任务会直接触发
五、Timer类的应用特性
- 运行时异常会导致timer线程终止
- 任务调度是基于绝对时间的,对系统时间敏感
六、如何解决任务阻塞问题?
解决方案
在自定义TimerTask里的run()方法里使用线程池去执行,即可解决上述问题
相关文章:

Jave 定时任务:使用Timer类执行定时任务为何会发生任务阻塞?如何解决?
IDE:IntelliJ IDEA 2022.2.3 x64 操作系统:win10 x64 位 家庭版 JDK: 1.8 文章目录 一、Timer类是什么?二、Timer类主要由哪些部分组成?1.TaskQueue2. TimerThread 三、示例代码分析四、自定义TimerTask为什么会发生任务相互阻塞的…...

Visual Studio Code配置c/c++环境
Visual Studio Code配置c/c环境 1.创建项目目录2.vscode打开项目目录3.项目中添加文件4.文件内容5.配置编译器6.配置构建任务7.配置调试设置 1.创建项目目录 d:\>mkdir d:\c语言项目\test012.vscode打开项目目录 3.项目中添加文件 4.文件内容 #include <iostream> u…...
漏洞利用工具的编写
预计更新网络扫描工具的编写漏洞扫描工具的编写Web渗透测试工具的编写密码破解工具的编写漏洞利用工具的编写拒绝服务攻击工具的编写密码保护工具的编写情报收集工具的编写 漏洞利用工具是一种常见的安全工具,它可以利用系统或应用程序中的漏洞来获取系统权限或者窃…...
ChatGPT之父被OpenAI解雇
首席技术官 Mira Murati 任命临时首席执行官领导 OpenAI;山姆阿尔特曼(Sam Altman)离开公司。 阿尔特曼先生的离职是在董事会经过深思熟虑的审查程序之后进行的,审查程序得出的结论是,他在与董事会的沟通中始终不坦诚…...

linux中利用fork复制进程,printf隐藏的缓冲区,写时拷贝技术,进程的逻辑地址与物理地址
1.prinf隐藏的缓冲区 1.思考:为什么会有缓冲区的存在? 2.演示及思考? 1).演示缓存区没有存在感 那为什么我们感觉不到缓冲区的存在呢?我们要打印东西直接就打印了呢? 我们用代码演示一下: 比如打开一个main.c,输入内容如下: #include <stdio.h>int main(){printf…...

java游戏制作-拼图游戏
一.制作主界面 首先创建一个Java项目命名为puzzlegame 结果:】 二.设置界面 代码: 三.初始化界面 代码: 优化代码: 四.添加图片 先在Java项目中创建图片文件夹,将图片导入其中 管理图片: 五.打乱图片顺序...
使用sklearn报AttributeError: ‘NoneType‘ object has no attribute ‘split‘
错误原因 在使用scikit-learn的时候报AttributeError: NoneType object has no attribute split Exception ignored on calling ctypes callback function: <function _ThreadpoolInfo._find_modules_with_dl_iterate_phdr..match_module_callback at 0x7fb757978160> T…...
C++学习 --map
目录 1, 什么是map 2, 创建map 2-1, 标准数据类型 2-2, 自定义数据类型 2-3, 其他创建方式 3, 操作map 3-1, 赋值 3-2, 插入元素(insert) 3-2-1, 插入标准数据类…...

基于Qt QList和QMap容器类示例
## QList<T> QList<T>容器是一个数组列表,特点如下: 1.大多数情况下可以用QList。像prepend()、append()和insert()这种操作,通常QList比QVector快的多。这是因为QList是基于index标签存储它的元素项在内存中(虽然内存不连续,这点与STL的list 是一样的),比…...

Flask学习一:概述
搭建项目 安装框架 pip install Flask第一个程序 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return "Hello World"if __name__ __main__:app.run()怎么说呢,感觉还不错的样子。 调试模式 if __name__ __main__:a…...
LeetCode:689. 三个无重叠子数组的最大和(dp C++)
目录 689. 三个无重叠子数组的最大和 题目描述: 实现代码与解析: dp 原理思路: 滑动窗口: 原理思路: 689. 三个无重叠子数组的最大和 题目描述: 给你一个整数数组 nums 和一个整数 k ,找…...

Leetcode—206.反转链表【简单】
2023每日刷题(三十三) Leetcode—206.反转链表 头插法实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* reverseList(struct ListNode* head) {if(head NULL…...
Linux - 内存 - 预留内存占用分析
说明 Linux启动log中会显示平台的内存信息,公司SOC平台,物理DRAM实际size是128M,但是启动log中total size不足128MB,并且预留内存(82272K reserved)过多,启动log如下: Memory: 480…...

Java学习之路 —— Java高级
文章目录 前言1. 单元测试2. 反射2.1 获取Class对象的三种方式2.2 获取类的构造器的方法2.3 获取类的成员变量2.4 获取类的成员方法2.5 反射的作用 3. 注解3.1 自定义注解3.2 注解的原理3.3 元注解3.4 注解的解析 4. 动态代理5. 总结 前言 终于走到新手村的末端了,…...

git使用及常用命令
在初入公司中,若使用的是git管理工具,需要做以下步骤: 1,常用命令在: (1),git config --global user.name xxx(名字) //若不设置 那么下次提交代码时会报错 其次该设置名字和…...
vue 学习 -- day36(分析工程结构)
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数 import { createApp } from vue import App from ./App.vue //创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”,它少了很多属性和方法) const app creat…...

SQL Injection
SQL Injection SQL injection(SQL注入),通过在输入字段或URL查询参数中执行SQL命令,导致对数据库的未经授权的访问。如果SQL注入成功,未经授权的人可能会读取、创建、更新甚至删除数据库表的记录 举个例子:…...

【Go入门】 Go搭建一个Web服务器
【Go入门】 Go搭建一个Web服务器 前面小节已经介绍了Web是基于http协议的一个服务,Go语言里面提供了一个完善的net/http包,通过http包可以很方便的搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,…...

VS 将 localhost访问改为ip访问
项目场景: 使用vs进行本地调试时需要多人访问界面,使用ip访问报错 问题描述 vs通过ip访问报错 虚拟机或其它电脑不能正常打开 原因分析: 原因是vs访问规则默认是iis,固定默认启动地址是localhost 解决方案: 1.vs项目启动之后会出现这个 右…...
app使用
font-face{font-family:‘kaishu’; src: url(data:application/font-ttf;charsetutf-8;base64,AAEAAAASAQAABAAgRFNJR5PpVzIAAAEsAAAacEdTVUIzhvftAAAbnAAAAXBPUy8yY8pHoQAAHQwAAABWY21hcAsTB9YAAB1kAADD5GN2dCAvAiIAADhSAAAA5pmcGdt/siFHQAA5OQAAAOiZ2FzcAAXAAkAAOiIAAAAEGds…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...