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

embedded/2024/10/19 9:32:50/

在这里插入图片描述

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任务类
java">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类
java">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接口

java">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 有一个线程来负责执行这里的任务
java">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);}}
java">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内的细节

java">    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;}}});}

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

java">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资源

java">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.ppmy.cn/embedded/19016.html

相关文章

[Spring Cloud] (5)gateway前后端公私钥与认证信息

文章目录 简述后端pom增加hutool工具类 nacos增加登录过期时间配置修改全局配置文件 安全通信认证接口控制层接口层实现层 工具类AES 对称加密工具类MD5工具类RSA非对称加密工具类加密盐工具类 前端引入jsencrypt工具类securityUtils.js 请求类系统通信密钥接口 登录接口增加认…

架构师系列- JVM(三)- 类加载

通过字节码&#xff0c;我们了解了class文件的结构 通过运行数据区&#xff0c;我们了解了jvm内部的内存划分及结构 接下来&#xff0c;让我们看看&#xff0c;字节码怎么进入jvm的内存空间&#xff0c;各自进入那个空间&#xff0c;以及怎么跑起来。 4.1 加载 4.1.1 概述 …

Qt tcp通信(客户端+服务器一对一)

学习自《Qt5.9 C开发指南》 服务器端&#xff1a; QTcpServer *tcpServer; //TCP服务器 tcpServernew QTcpServer(this); connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection())); 当有新的客户端接入时&#xff0c;QTcpServer内部的incomingConnectio…

Day41 HTTP编程

Day41 HTTP编程 文章目录 Day41 HTTP编程HTTP概念应用场景主要方面 HTTP案例案例一&#xff1a;获取淘宝商品周边类别案例二&#xff1a;下载图片 HTTP 概念 HTTP编程指的是使用HTTP协议进行网络编程的过程。HTTP是一种用于传输超文本的应用层协议&#xff0c;通常用于在客户…

QML与C++交互

Qt 你好 | 专注于Qt的技术分享平台 QML写界面&#xff0c;业务逻辑使用C&#xff0c;既能快速的开发界面也能利用C的强大生态&#xff0c;这是目前比较被认可的方式&#xff0c;那就涉及到QML与C对象的交互。 我们以登录例子来说明&#xff0c;页面点击登录&#xff0c;将信息…

区块链技术:NFG元宇宙电商模式

大家好&#xff0c;我是微三云周丽 随着互联网技术的迅猛发展&#xff0c;电子商务行业逐渐崛起为现代经济的重要支柱。而在这一浪潮中&#xff0c;元宇宙电商以其独特的商业模式和巨大的发展潜力&#xff0c;成为行业的新宠。其中&#xff0c;NFG作为元宇宙电商模式的代表&am…

鸿蒙(HarmonyOS)性能优化实战-多线程共享内存

概述 在应用开发中&#xff0c;为了避免主线程阻塞&#xff0c;提高应用性能&#xff0c;需要将一些耗时操作放在子线程中执行。此时&#xff0c;子线程就需要访问主线程中的数据。ArkTS采用了基于消息通信的Actor并发模型&#xff0c;具有内存隔离的特性&#xff0c;所以跨线…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下&#xff1a; #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…