JavaEE初阶——多线程(七)——定时器

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享多线程的第七篇文章——关于定时器
如果有不足的或者错误的请您指出!

目录

    • 4.定时器
      • 4.1标准库提供的定时器
      • 4.2自己实现一个定时器
        • 4.2.1任务类
        • 4.2.2Timer类
        • 4.2.3 有一个线程来负责执行这里的任务

4.定时器

所谓定时器就是类似于闹钟效果,指定一个任务给他,这个任务不会立即执行.而是到达指定的时间后才执行
定时器在实际开发中非常重要,甚至会单独封装成一个服务器,给整个分布式系统使用

4.1标准库提供的定时器

在这里插入图片描述
这里的TimeTask实际上是继承了Runnable接口的
在这里插入图片描述
同时,Timer内部包含的也是前台线程,阻止了进程结束

4.2自己实现一个定时器

需求:能够延迟执行任务 ,能够管理多个任务

需要有:定义一个类.表示一个任务

通过一定的数据结构来保存多个任务

还需要有一个线程,来负责执行这里的任务(在指定之间内去执行)

4.2.1任务类
public class MyTimeTask {/*当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)为了后续线程执行的时候,可以方便的判定,该任务是否执行*/private long time;private Runnable runnable;public MyTimeTask(Runnable runnable ,long delay) {this.time = System.currentTimeMillis() + delay;//手动换算时间this.runnable = runnable;}
}
4.2.2Timer类
public class MyTimer {/*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口*/PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();public void schedule(Runnable runnable,int delay) {MyTimeTask timeTask = new MyTimeTask(runnable,delay);queue.offer(timeTask);}
}

此时别忘了对MyTimeTask类实现Comparable接口

public class MyTimeTask implements Comparable<MyTimeTask>{/*当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)为了后续线程执行的时候,可以方便的判定,该任务是否执行*/private long time;private Runnable runnable;public MyTimeTask(Runnable runnable ,long delay) {this.time = System.currentTimeMillis() + delay;//手动换算时间this.runnable = runnable;}@Overridepublic int compareTo(MyTimeTask o) {return (int)(this.time - o.time);}
}
4.2.3 有一个线程来负责执行这里的任务
public class MyTimer {/*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口*/PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();public MyTimer () {Thread t = new Thread (() -> {while(true) {//此时就要判断当前优先级任务队列的头任务是否到达执行时间了//如果到达则执行,不到达则循环判断}});}public void schedule(Runnable runnable,int delay) {MyTimeTask timeTask = new MyTimeTask(runnable,delay);queue.offer(timeTask);}}
public class MyTimeTask implements Comparable<MyTimeTask>{/*当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)为了后续线程执行的时候,可以方便的判定,该任务是否执行*/private long time;private Runnable runnable;public MyTimeTask(Runnable runnable ,long delay) {this.time = System.currentTimeMillis() + delay;//手动换算时间this.runnable = runnable;}public void run() {this.runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimeTask o) {return (int)(this.time - o.time);}
}

那么我们就来具体实现schedule内的细节

    public MyTimer () {Thread t = new Thread (() -> {while(true) {//此时就要判断当前优先级任务队列的头任务是否到达执行时间了//如果到达则执行,不到达则循环判断if(queue.isEmpty()) {continue;}long curTime = System.currentTimeMillis();MyTimeTask task = queue.peek();if(task.getTime() <= curTime) {task.run();queue.poll();}else{//时间未到continue;}}});}

但是此时可能存在不同线程同时修改同一个队列的情况,就要加入锁

public class MyTimer {/*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口*/PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer () {Thread t = new Thread (() -> {synchronized (locker) {while(true) {//此时就要判断当前优先级任务队列的头任务是否到达执行时间了//如果到达则执行,不到达则循环判断if(queue.isEmpty()) {continue;}long curTime = System.currentTimeMillis();MyTimeTask task = queue.peek();if(task.getTime() <= curTime) {task.run();queue.poll();}else{//时间未到continue;}}}});}public void schedule(Runnable runnable,int delay) {synchronized (locker) {MyTimeTask timeTask = new MyTimeTask(runnable,delay);queue.offer(timeTask);}}
}

此时还存在两个比较核心的问题:

(1)上述的循环等待,实际上这个代码逻辑处于"忙等"的状态,确实是在等,但是等的过程中很忙,比如14:00要执行任务,但是13:00就开始等了

上述代码在短时间内疚会循环很多次,上述操作都是在"空转",一直在消耗cpu,没有真正执行任务

而我们要实现的就是在等待的时间内,要释放cpu资源

public class MyTimer {/*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口*/PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer () {Thread t = new Thread (() -> {try {while(true) {synchronized (locker) {//此时就要判断当前优先级任务队列的头任务是否到达执行时间了//如果到达则执行,不到达则循环判断if(queue.isEmpty()) {locker.wait();//队列为空,等待,直到有新的任务进来}long curTime = System.currentTimeMillis();MyTimeTask task = queue.peek();if(task.getTime() <= curTime) {task.run();queue.poll();}else{locker.wait(task.getTime() - curTime);//时间未到,等待,直到有新的任务进来(判断新的任务是否要执行)//或者时间到了,执行}}}}catch(InterruptedException e) {e.printStackTrace();}});}public void schedule(Runnable runnable,int delay) {synchronized (locker) {MyTimeTask timeTask = new MyTimeTask(runnable,delay);queue.offer(timeTask);locker.notify();}}
}

为什么这里不使用PriorityBlockingQueue呢??

实际上不如手动加锁,因为引入阻塞队列只能解决队列为空的阻塞,而时间没到的阻塞还是要我们自己去实现,还但是要引入新的锁,代码就搞复杂了,并且阻塞队列里面本来就有一把锁,这样反而可能导致死锁的出现

这里的wait能不能换成sleep?? ----不行!!!

notift唤醒wait,属于常规手段,是我们处理正常业务的流程,但是sleep通过interrupt唤醒,是处理异常业务的

此外,更加致命的是,wait休眠期间会释放锁,但是sleep可不会释放锁

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

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

相关文章

如何解决IntelliJ IDEA 2024打开项目时频繁闪退问题

&#x1f42f; 如何解决IntelliJ IDEA 2024打开项目时频繁闪退问题 &#x1f43e; 文章目录 &#x1f42f; 如何解决IntelliJ IDEA 2024打开项目时频繁闪退问题 &#x1f43e;摘要引言正文&#x1f4d8; 识别问题&#x1f4d9; 内存配置调整步骤1: 定位vmoptions文件步骤2: 修改…

svg图标填充渐变色及CSS鼠标悬停纯色渐变色转换

svg图标填充渐变色及CSS鼠标悬停纯色渐变色转换&#xff1a; HTML&#xff1a; <!--底部导航--> <ul class"milliaNav"> <li class"active"><a href"#"> <svg class"icon" viewBox"0 0 1024 1024&qu…

1分钟掌握 Python 函数参数

任何编程语言函数都是非常重要的一部分&#xff0c;而在进行函数调用时&#xff0c;了解函数的参数传递方式是非常有必要的。Python中支持哪些传参方式呢&#xff1f; Python中的传参方式是比较灵活的&#xff0c;主要包括以下六种&#xff1a; 按照位置传参按照关键字传参默…

力扣--N皇后

题目: 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。…

李彦宏:程序员将不复存在! 周鸿祎回怼!网友:先把百度程序员都开除了!

近日&#xff0c;百度创始人、董事长兼首席执行官李彦宏在央视《对话》•开年说的访谈中指出&#xff1a;“基本上说以后其实不会存在“程序员”这种职业了&#xff0c;因为只要会说话&#xff0c;人人都会具备程序员的能力”。 “未来的编程语言只会剩下两种&#xff0c;一种…

Java进阶-异常处理

概述 常见运行时异常 直接继承自RuntimeException或者子类&#xff0c;编译阶段不会报错&#xff0c;运行时可能出现的错误 运行时异常&#xff1a;一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误 数组索引越界异常空指针异常类型转换异常数学操作异常数字转…

离线语音模块初步学习——LSYT201B(深圳雷龙发展)

一 、产品简介 首先简单介绍下该离线语音模块&#xff0c;官方给出的介绍是&#xff1a;YT2228 是根据智能语音交互市场需求及思必驰算法的发展方向定义开发的 “芯片算法”人工智能人机语音交互解决方案&#xff0c;具有高性能、低功耗等特点。该芯片通过软硬融合的方法&…

数据丢失的谜团:文件突然不见了,却还占用着空间

在数字化时代&#xff0c;数据的重要性不言而喻。然而&#xff0c;有时我们会遇到一个令人困惑的问题&#xff1a;明明存储在设备中的文件突然不见了&#xff0c;但磁盘空间仍被占用。这种情况究竟是怎么回事&#xff1f;我们又该如何找回这些“消失”的文件呢&#xff1f;接下…

ROS python实现乌龟跟随

产生两只乌龟&#xff0c;中间的乌龟(A) 和 左下乌龟(B), B 会自动运行至A的位置&#xff0c;并且键盘控制时&#xff0c;只是控制 A 的运动&#xff0c;但是 B 可以跟随 A 运行 乌龟跟随实现的核心&#xff0c;是乌龟A和B都要发布相对世界坐标系的坐标信息&#xff0c;然后&am…

力扣HOT100 - 994. 腐烂的橘子

解题思路&#xff1a; 因为要记录轮数&#xff08;分钟数&#xff09;&#xff0c;所以不能一口气遍历到底&#xff0c;所以不能用深搜&#xff08;bfs&#xff09;&#xff0c;而要用广搜&#xff08;bfs&#xff0c;层序遍历&#xff09;。 先记录下新鲜橘子数&#xff0c;…

理解控制反转

好久之前写的学习笔记&#xff0c;一直落在草稿箱里今天才发现&#xff0c;既然写了就补发出来吧~ 当需要实现不同操作时&#xff0c;用户和程序员都无需修改&#xff0c;只需要修改xml配置即可。 代码体现&#xff1a; 不同接口的实现类&#xff1a; xml具体配置 通过set注入…

GO语言写Prometheus自定义node-exporter的Docker容器测试

1. 安装docker-compose 执行以下命令&#xff0c;安装docker-compose到CentOS7.9环境中&#xff1a; # 下载二进制文件 sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.7/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/d…

Qt : 在QTreeWidget中添加自定义右键菜单

一、引言 如图&#xff0c;我们需要在一个QTreeWidget 控件中添加了自定义右键菜单。 二、思路 如何做到的呢&#xff0c;很简单。浅浅记录和分享一下。 继承QTreeWidget&#xff0c;定义一个子类CustomTreeWidget &#xff0c;在重写contextMenuEvent 事件即可。 三、代…

linux调试-访问物理地址

1. devmem 方式 rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0xa rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0x0A rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0xb rootraspberrypi:/home/niyu# busybox devmem 0x7e21500…

如何制作个性又美观的二维码?自定义Logo、样式,还能一键复用

草料二维码提供基础的二维码美化设置&#xff0c;包含Logo、颜色、码点码眼、容错、添加文字等设置。 还提供150标签样式&#xff0c;标签样式中所有内容&#xff0c;包括LOGO、背景、字段数量等&#xff0c;均可修改。 同时&#xff0c;支持将样式保存到账号下&#xff0c;方…

从递归角度串联二叉树-图论-动态规划

一、深度理解二叉树的前中后序遍历 二叉树遍历框架如下&#xff1a; void traverse(TreeNode* root) {if (root nullptr) {return;}// 前序位置traverse(root->left);// 中序位置traverse(root->right);// 后序位置 }先不管所谓前中后序&#xff0c;单看 traverse 函数…

常见的工业路由器访问问题

A&#xff1a;工业路由器已经设置了pptp怎么访问路由下面的电脑 1. 确认PPTP VPN设置&#xff1a;首先&#xff0c;确保PPTP VPN服务器在工业路由器上已正确设置&#xff0c;并且处于活动状态。这包括确保VPN服务器的IP地址、端口、用户名和密码等设置正确无误。 2. 连接到VP…

硬件24、嘉立创EDA丝印的优化和调整

1、调整全部丝印的属性 先选中一个丝印&#xff0c;然后右键点击它&#xff0c;选择查找&#xff0c;然后选择查找全部 选择查找全部这个时候可以设置所有丝印在元件的位置了&#xff0c;布局-》属性位置&#xff0c;位号&#xff0c;属性位置设置为上边&#xff0c;这时丝印就…

五、yolov8 tensorRT c++部署及接口封装(保姆级教程附源码)

采用 CTensorRT来部署深度学习模型有以下几个优点&#xff1a; 高性能推理&#xff1a;TensorRT是一个高性能的深度学习推理&#xff08;Inference&#xff09;优化器&#xff0c;专门为NVIDIA GPU硬件平台设计&#xff0c;能够提供低延迟、高吞吐量的模型推理性能。这意味着在…

深度学习pytorch实战4---猴逗病识别·

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/)** 引言 1.复习上周并反思 K同学针对大家近…