【JavaEE】【多线程】定时器
目录
- 一、定时器简介
- 1.1 Timer类
- 1.2 使用案例
- 二、实现简易定时器
- 2.1 MyTimerTask类
- 2.2 实现schedule方法
- 2.3 构造方法
- 2.4 总代码
- 2.5 测试
一、定时器简介
定时器:就相当于一个闹钟,当我们定的时间到了,那么就执行一些逻辑。
1.1 Timer类
Java的标准库中提供了在java.util包下的Timer类作为定时器。
有如下的构造方法:
四种:
- timer() 无参构造;
- timer(boolean isDaemon) 创建的线程都是后台线程;
- timer(String name) 给定时器中创建的线程名字;
- timer(String name, boolean isDaemon) 创建的线程都是后台线程,也给定时器中创建的线程名字。

在Timer类中的核心方法是schedule方法。
- schedule(Timer task, Date time) 到达time时刻后执行task任务;
- schedule(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次相隔period时间;
- schedule(Timer task, long delay) 在delay时间后执行task任务;
- schedule(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次相隔period时间;
- scheduleAtFixedRate(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次执行period时间;
- scheduleAtFixedRate(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次执行period时间;

schedule的第一个参数是TimerTask类,这是一个实现了Runnable接口的抽象类。

1.2 使用案例
我们使用schedule方法来打印不同时间执行不同内容。
import java.util.Timer;
import java.util.TimerTask;public class Demo {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("3000ms后执行");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1000ms后执行");}},1000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000ms后执行");}},2000);}
}
结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。

二、实现简易定时器
自己实现的定时器主要要考虑下面几个内容:
- 设计一个类表示任务,对应TimerTask类;
- 使用优先级队列来组织多个任务,每次根节点都是等待时间最短的任务;
- 实现schedule方法,把任务添加到队列中;
- 额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。
2.1 MyTimerTask类
这个类中需要:
- 将要执行的任务,和任务要执行的时刻记录下来,
- 并且这个任务还要有通过时刻比较得方法(即实现Comparator接口,重写CompareTo方法),便于后面存储进优先级队列。
代码:
class MyTimerTask implements Comparable<MyTimerTask>{//记录任务private Runnable task = null;//记录执行任务的时刻private long current = 0;public MyTimerTask(Runnable task, long current) {this.task = task;this.current = current;}public Runnable getTask() {return task;}public long getCurrent() {return current;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.current - o.current);}
}
2.2 实现schedule方法
我们实现schedule方法:
- 只需要将当前的任务传入队列中即可。
- 将参数Runnable的任务和时刻用来创建MyTimerTask类,在入队即可。
- 我们还要使用notify为后面的线程中因为队列为空调用wait进入阻塞状态提供唤醒。
代码:
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public void schedule(Runnable task, long delay) {synchronized (this) {MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(myTimerTask);this.notify();}}
2.3 构造方法
在构造方法中额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。
- 我们在最外层使用一层死循环来不断去读取队列中的任务。
- 如果队列空了,那么我们就出这次循环,但是如果使用continue的话,还是会在循环的去判断直到队列不为空为止。这样的消耗很高,我们可以使用wait等待schedule方法入队列后;来唤醒这个线程。
- 如果没有到达执行时间,我们也要出这次循环,但是使用continue也会导致在从现在这个时刻到执行时刻之间一直进行无意义的执行上面的代码,消耗很高,我们这里直接使用带参数的wait方法等待还需要的时间即可。
- 到达执行时间直接执行任务并出队列即可。
- 最后不要忘记启动这个线程。
代码:
public MyTimer() {Thread thread = new Thread(()-> {try {while(true) { //循环拿任务,直到任务队列为空synchronized (this) {while (queue.isEmpty()) { //任务队列为空this.wait();}MyTimerTask task = queue.peek();if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间 this.wait(task.getCurrent() - System.currentTimeMillis());} else {task.run();queue.poll();}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
2.4 总代码
总代码如下:
class MyTimerTask implements Comparable<MyTimerTask>{//记录任务private Runnable task = null;//记录执行任务的时刻private long current = 0;public MyTimerTask(Runnable task, long current) {this.task = task;this.current = current;}public Runnable getTask() {return task;}public long getCurrent() {return current;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.current - o.current);}public void run() {task.run();}}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public void schedule(Runnable task, long delay) {synchronized (this) {MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(myTimerTask);this.notify();}}public MyTimer() {Thread thread = new Thread(()-> {try {while(true) { //循环拿任务,直到任务队列为空synchronized (this) {while (queue.isEmpty()) { //任务队列为空this.wait();}MyTimerTask task = queue.peek();if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间this.wait(task.getCurrent() - System.currentTimeMillis());} else {task.run();queue.poll();}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
}
2.5 测试
如果在main中执行下面这样的代码,也使用schedule方法来打印不同时间执行不同内容,会与上面使用案例的结果一样。
public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000ms后执行");}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000ms后执行");}},1000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2000ms后执行");}},2000);}
结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。

相关文章:
【JavaEE】【多线程】定时器
目录 一、定时器简介1.1 Timer类1.2 使用案例 二、实现简易定时器2.1 MyTimerTask类2.2 实现schedule方法2.3 构造方法2.4 总代码2.5 测试 一、定时器简介 定时器:就相当于一个闹钟,当我们定的时间到了,那么就执行一些逻辑。 1.1 Timer类 …...
CI/CD 的原理
一、CI/CD 的概念 CI/CD是一种软件开发流程,旨在通过自动化和持续的集成、测试和交付实现高质量的软件产品。 CI(Continuous Integration)持续集成 目前主流的开发方式是协同开发,即多位开发人员同事处理同意应用不同模块或功能。 如果企业在同一时间将…...
进一步认识ICMP协议
在日常工作中,我们经常需要判断网络是否连通,相信大家使用较多的命令就是 ping啦。ping命令是基于 ICMP 协议来实现的,那么什么是 ICMP 协议呢?ping命令又是如何基于 ICMP 实现的呢? 今天这篇文章,我们就来…...
NUUO网络视频录像机upload.php任意文件上传漏洞复现
文章目录 免责声明漏洞描述搜索语法漏洞复现nuclei修复建议 免责声明 本文章仅供学习与交流,请勿用于非法用途,均由使用者本人负责,文章作者不为此承担任何责任 漏洞描述 NUUO网络视频录像机(Network Video Recorder࿰…...
WebGL 3D基础
1. 归一化函数 对一个向量进行归一化处理,即调整向量的模长(长度)为1,同时保持其方向不变。 // 归一化函数 function normalized(arr) {let sum 0;for (let i 0; i < arr.length; i) {sum arr[i] * arr[i];}const middle …...
Docker 部署MongoDb
1. 编写docker-compose.conf 文件 version: 3 services:mongo:image: mongo:latest # 指定 MongoDB 版本,确保 > 3.6container_name: mongo-replicarestart: alwayscommand: ["mongod", "--replSet", "rs0", "--oplogSize&…...
【Hadoop】hadoop的路径分不清?HDFS路径与本地文件系统路径的区别
/usr/local/hadoop /user/hadoop /home/hadoop/ 这里有些路径名很相似,帮我区分? 在Hadoop生态系统中,理解文件存储的位置对于有效管理数据至关重要。Hadoop分布式文件系统(HDFS)提供了一个高度可靠的存储系统…...
倪师学习笔记-天纪-易经八卦
一、简介 卦代表事情,爻代表时机,三爻为一卦八卦对应的天相,六十四卦对应人间事 二、八卦性 1、乾 天父亲向下看,无所求,雄心万丈始终如一,贞,坚心,专心至刚,天威&am…...
自动驾驶性能分析时,非常有用的两个信息
自动驾驶的关键路径如下,传感器的数据发送给感知模块;感知模块根据传感器数据来确定车辆所处的环境,比如前方有没有障碍物,是不是和车道线保持着适当的距离等;感知处理之后的数据传递给规控模块,规控根据车…...
数据结构 - 并查集
文章目录 一、并查集原理二、并查集实现三、并查集的应用 一、并查集原理 在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复…...
canvas基础+应用+实例
文章目录 Canvas基础知识要点一、基本概念二、常用参数三、实例四、场景应用说明完结 Canvas基础知识要点 一、基本概念 Canvas是HTML5中的一个标签,用于在网页上通过JavaScript绘制图形、动画等。它提供了一个空白的、基于像素的绘图区域,就像一块画布…...
Linux命令 用户操作简介
目录 1. 添加新的用户账号 2. 删除用户账号 3. 修改用户账号 4. 用户口令的管理 示例汇总 添加新用户 删除用户 修改用户信息 更改用户口令 在 Linux 系统中,用户管理是一项重要的任务,包括添加新用户、删除用户、修改用户信息以及管理用户口令…...
大语言模型的Scaling Law【Power Low】
NLP-大语言模型学习系列目录 一、注意力机制基础——RNN,Seq2Seq等基础知识 二、注意力机制【Self-Attention,自注意力模型】 三、Transformer图文详解【Attention is all you need】 四、大语言模型的Scaling Law【Power Low】 文章目录 NLP-大语言模型学习系列目录一、什么是…...
windows环境下,使用docker搭建redis集群
参考: https://blog.csdn.net/weixin_46594796/article/details/137864842 https://www.cnblogs.com/niceyoo/p/14118146.html 史上最详细Docker搭建Redis Cluster集群环境 值得收藏 每步都有图,不用担心学不会-腾讯云开发者社区-腾讯云 一、基础环境描述 宿主机:192.168…...
Python(pandas库3)
函数 随机抽样 语法: n:要抽取的行数 frac:抽取的比例,比如 frac0.5,代表抽取总体数据的50% axis:示在哪个方向上抽取数据(axis1 表示列/axis0 表示行) 案例: 输出结果都为随机抽取。 空…...
WPF+MVVM案例实战(十)- 水波纹按钮实现与控件封装
文章目录 1、运行效果1、封装用户控件1、创建文件2、依赖属性实现2、使用封装的按钮控件1.主界面引用2.按钮属性设置3 总结1、运行效果 1、封装用户控件 1、创建文件 打开 Wpf_Examples 项目,在 UserControlLib 用户控件库中创建按钮文件 WaterRipplesButton.xaml ,修改 Us…...
数据结构————map,set详解
今天带来map和set的详解,保证大家分清楚 一,概念 map和set是一种专门用来搜索的容器或数据结构 map能存储两个数据类型,我们称之为<key-value>模型 set只能存储一个数据类型,我们称之为纯<key>模型 它们的效率都非…...
fdisk - Linux下的磁盘分区利器
文章目录 前言一、安装和启动二、基本命令2.1 查看分区表2.2 删除分区2.3 创建新分区2.4 更改分区类型2.5 其他指令 三、注意事项四、其他相关工具 前言 在Linux系统中,磁盘管理是维护系统性能和数据安全的重要环节。fdisk 是一个强大的命令行工具,专门…...
or-tools优化库记录
介绍 Or-tools是谷歌人工智能系列的运筹优化包,是一个用于优化的开源软件套件,针对性地解决车辆路线问题、流程优化、整数和线性规划以及约束规划等问题。 官网地使用说明比我详细,我就不多逼逼了 使用说明网址: https://develo…...
M1 Pro MacBook Pro 上的奇遇:Rust 构建失败,SIGKILL 惊魂记
你是否也曾在 M1 Pro MacBook Pro 上遇到过离奇的编译问题?这次我遇到的奇葩问题绝对值得一聊——一个仅在苹果M1 Pro上的神秘构建失败。其他设备都安然无恙,唯独它!折腾了一番,终于让我揭开了这“阴谋”的真相。 问题描述 在运…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
