「JavaEE」多线程案例分析2:实现定时器

🎇个人主页:Ice_Sugar_7
🎇所属专栏:JavaEE
🎇欢迎点赞收藏加关注哦!

实现定时器

  • 🍉简介
  • 🍉模拟实现定时器

🍉简介

定时器类似一个闹钟,时间到了之后就会执行相应的任务
Java 标准库中已经实现了一个定时器的类 Timer

Timer timer = new Timer();

在定义好 timer 之后可以调用 schedule 把一个或多个任务(TimerTask)添加到定时器中

timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000 ms");}
},2000);

第一个参数就是任务内容,每个任务后面都会带有一个时间(第二个参数),这个时间是“相对时间”,是以 schedule 时的时间为基准,过了相对时间后才执行
比如 2000ms,它表示调用 schedule 后再过 2000ms 就会执行这个任务

TimerTask 里面有一个 run 方法,而 run 是线程的入口,说明 timer 创建了一个线程来执行任务。这个线程是前台线程,它会阻止主线程结束,需要我们使用 cancel 主动结束,否则 Timer 不知道其他地方是否会继续添加任务

public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000 ms");timer.cancel(); //结束线程}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1000 ms");}},1000);
}

在这里插入图片描述


🍉模拟实现定时器

首先要有一个数据结构负责保存 schedule 的任务(相当于任务清单),因为我们是先执行时间近的任务(比如有两个任务,一个是两点执行,另一个是两点半执行,肯定要先完成前者),换而言之,任务之间是有优先级的,所以要用优先级队列
标准库中提供了 PriorityQueue 和 PriorityBlockingQueue,前者是线程不安全的,后者是线程安全的,在此处的场景中 PriorityBlockingQueue 不太好控制,容易出问题,所以我们用前者

(补充:TreeSet 和 TreeMap 虽然也是有序的,但是获取到最小值的时间复杂度为 O(logN),不及 O(1) 的优先级队列)

然后需要有一个线程不断扫描优先级队列的队首元素,看它时间到了没

public class MyTimer {private Thread t;private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer(){//定时器构造方法的主体就是启动线程,让它去扫描队首元素t = new Thread(() -> {while (true) {synchronized (locker) {while (queue.isEmpty()) {try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime >= task.getTime()) { //时间到了,执行任务task.run();queue.poll(); //记得执行后把它出队列} else {try {locker.wait(task.getTime() - curTime); //如果还没到执行时间,那就等待,不要一直循环下去} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});t.start();}public void schedule(Runnable runnable,long delay) { //把任务添加到 queue 里面synchronized (locker) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);locker.notify(); //添加元素后,就可以唤醒处于 wait 状态的线程}}
}

队列的元素——任务,它是一个类。它的成员变量应该包括时间、能让它跑起来的 Runnable 接口

public class MyTimerTask{private long time; //执行任务的时间(注意这个是“绝对时间”)private Runnable runnable; //持有 Runnable 接口可以调用它的 run 方法,也可以不持有 Runnable,而是实现 Runnable 接口并重写 run 方法MyTimerTask(Runnable runnable,long time) {this.runnable = runnable;this.time = time + System.currentTimeMillis(); //什么时候执行任务:现在的时间 + 相对时间}public void run() {runnable.run();}
}

因为优先级队列要求元素是可排序的,所以我们需要实现 Comparable 接口并重写 compareTo 方法

public class MyTimerTask implements Comparable<MyTimerTask>{private long time; //执行任务的时间(注意这个是“绝对时间”)private Runnable runnable; //持有 Runnable 接口可以调用它的 run 方法,也可以不持有 Runnable,而是实现 Runnable 接口并重写 run 方法MyTimerTask(Runnable runnable,long time) {this.runnable = runnable;this.time = time + System.currentTimeMillis(); //什么时候执行任务:现在的时间 + 相对时间}public void run() {runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int) (o.time-this.time); //时间小的优先级更高}
}

补充:compareTo 方法里面是 o.time-this.time 还是 this.time - o.time,不用去刻意记忆,两种都试一下就 ok 了

测试一下:

public class TestDemo {public static void main(String[] args) throws InterruptedException {MyTimer myTimer = new MyTimer();myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000 ms");}},3000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000 ms");}},1000);}
}

在这里插入图片描述

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

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

相关文章

重生我是嵌入式大能之串口调试UART

什么是串口 串口是一种在数据通讯中广泛使用的通讯接口&#xff0c;通常我们叫做UART (通用异步收发传输器Universal Asynchronous Receiver/Transmitter)&#xff0c;其具有数据传输速度稳定、可靠性高、适用范围广等优点。在嵌入式系统中&#xff0c;串口常用于与外部设备进…

MTATLAB--一元线性回归分析

一文让你彻底搞懂最小二乘法&#xff08;超详细推导&#xff09; 在进行一元线性回归分析时&#xff0c;使用最小二乘法进行解题&#xff0c;关于最小二乘法具体看上述文章。 数据文件在文章顶部可见&#xff0c;将第一列数据作为自变量x&#xff0c;第二列数据作为应变量y。建…

3款常用的可视化工具Matplotlib、Seaborn和Pandas

大家好&#xff0c;Seaborn 是基于 Matplotlib 的扩展库&#xff0c;Pandas 的可视化功能同样也依赖于 Matplotlib。尽管二者都使用相同的底层图形库&#xff0c;但绘制图表的方法却各有千秋。本文将介绍各种柱状图的绘制&#xff0c;比较 Matplotlib、Pandas 和 Seaborn 在数据…

手机号码的正则表达式

手机号码的正则表达式会根据不同的国家/地区有所不同&#xff0c;因为每个国家/地区都有自己特定的手机号码格式。但是&#xff0c;我可以为你提供一个通用的正则表达式模板&#xff0c;并给出一些具体国家/地区的例子。 通用模板 一个基本的手机号码正则表达式模板可能如下所…

vulhub靶机struts2环境下的s2-032(CVE-2016-3081)(远程命令执行漏洞)

影响范围 Struts 2.3.19至2.3.20.2、2.3.21至2.3.24.1和2.3.25至2.3.28 当用户提交表单数据并验证失败时&#xff0c;后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析&#xff0c;然后重新填充到对应的表单数据中。 漏洞搭建 没有特殊要求&#xff0c;请看 (3…

autoxjs的介绍

AutoX.js 是一个基于 JavaScript 的自动化工具&#xff0c;用于在 Android 平台上创建自动化脚本。以下是关于 AutoX.js 的一些关键介绍&#xff1a; 1. **无需Root权限**&#xff1a;AutoX.js 可以在没有 Root 权限的设备上运行&#xff0c;利用 Android 的无障碍服务来实现自…

EasyImage2.0 图床源码

EasyImage2.0 是一个简单图床的源码&#xff0c;它支持以下功能&#xff1a; 1. API接口 2. 登录后才能上传图片 3. 设置图片质量 4. 压缩图片大小 5. 添加文字或图片水印 6. 设定图片的宽度和高度 7. 将上传的图片转换为指定的格式 8. 限制上传图片的最小宽度和高度 …

了解 Docker: Docker、Docker Desktop 与 Docker Engine 之间的联系

了解 Docker: Docker、Docker Desktop 与 Docker Engine 之间的联系 本文将探讨 Docker 的核心概念&#xff1a;Docker、Docker Desktop 和 Docker Engine&#xff0c;以及它们之间的联系。 Docker: 容器化的先驱 Docker 是一个开源平台&#xff0c;用于开发、运送和运行应用…

11_进程管理和SELinux

什么是进程 在系统中触发任何一个事件&#xff0c;系统都会把它定义成一个进程&#xff0c;并且给这个进程一个ID&#xff0c;也就是PID。同时也会根据触发这个进程的用户与相关属性&#xff0c;给这个PID一个相应的权限设置。 进程与程序 如何触发事件呢&#xff0c;执行程…

【LangChain学习之旅】—(21)聊天客服机器人的开发(上)

【LangChain学习之旅】—(21)聊天客服机器人的开发(上) “聊天机器人”说明项目的技术实现细节技术实现步骤简述第二步:增加记忆机制第三步:增加检索机制总结“聊天机器人”说明 聊天机器人(Chatbot)是 LLM 和 LangChain 的核心用例之一,很多人学习大语言模型,学习 …

Java入门基础学习笔记19——关系运算符、逻辑运算符

关系运算符&#xff1a; 判断数据是否满足条件&#xff0c;最终会返回一个判断的结果&#xff0c;这个结果是布尔类型的值&#xff1a;true或false。 注意&#xff1a;在java中判断是否相等一定是“”&#xff0c;不要把“”写成“”&#xff0c;“”是赋值表达式。 package c…

社区新零售:家门口的便利与温暖

社区新零售&#xff1a;家门口的便利与温暖 随着都市生活节奏的加快&#xff0c;人们对于便捷、高效的生活方式有了更高的追求。社区新零售&#xff0c;作为零售业的一股新兴力量&#xff0c;正以其独特的魅力&#xff0c;悄然改变着我们的日常生活。 家门口的便利 社区新零…

嵌入式学习第三十三天!(二叉树)

1. 树的概念&#xff1a; 1. 树&#xff1a;由n个结点组成的有限集&#xff0c;有且只有一个根结点&#xff08;由根结点可以访问后继结点&#xff09;&#xff0c;其他结点只有一个前驱结点&#xff0c;但可以有多个后继结点&#xff08;一对多&#xff09;。当n 0时&#xf…

关于企鹅云

千万别用成都机房的云服务器&#xff01;忠告&#xff01;

OpenAI 刚刚宣布了 “GPT-4o“ 免费用户开放、通过 API 可用

OpenAI 刚刚宣布了 “GPT-4o”。它可以通过语音、视觉和文本进行推理。 该模型速度提高了 2 倍&#xff0c;价格降低了 50%&#xff0c;比 GPT-4 Turbo 的速率限制高出了 5 倍。 它将对免费用户开放、通过 API 可用。 与 GPT-4 相比&#xff0c;GPT-4o 的速度和额外的编码能力…

揭秘APP广告:变现逻辑全解析!

在当今的移动互联网时代&#xff0c;APP广告变现已经成为了各大应用开发者的主要营收来源之一。然而&#xff0c;随着科技的发展、用户行为的变化以及广告市场趋势的演进&#xff0c;APP广告变现逻辑也正在不断地进行优化和调整。本文将基于当前市场和技术趋势&#xff0c;为大…

Proteus新手入门之初学体验

Proteus是嵌入式工程师比较喜欢用的&#xff0c;可以实现从原理图布图、代码调试到单片机与外围电路协同仿真。作为一款功能强大的电子电路仿真软件&#xff0c;Proteus为电子爱好者和工程师们提供了一个理想的平台&#xff0c;用于设计、测试和验证各种电子电路。对于初学者来…

CSS 块状元素

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

Saas详解

1. 什么是Saas SaaS&#xff08;Software-as-a-Service&#xff09;&#xff0c;简单点理解就是软件即服务&#xff0c;即通过网络提供软件服务。 在SaaS模型中&#xff0c;用户不需要在本地安装软件&#xff0c;而是通过网络&#xff08;通常是浏览器&#xff09;访问应用程…

JVM的垃圾回收机制及其工作原理

Java 虚拟机&#xff08;JVM&#xff09;的垃圾回收机制&#xff08;GC&#xff09;旨在自动管理应用程序申请的内存。当对象不再被使用时&#xff0c;GC会自动释放它们所占据的堆内存&#xff0c;防止内存泄漏。 JVM内存区域&#xff1a; 在了解垃圾回收之前&#xff0c;要知…