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.hqwc.cn/news/641600.html

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

相关文章

Oracle数据库的简单使用

Oracle简单使用 一、数据库的介绍二、Oracle介绍账号管理Oracle的安装Oracle服务的作用OracleRemExecService服务创建数据库 常用命令 三、SQL语言SQL分类实用的数据表添加注释数据操纵语言&#xff08;DML&#xff09;查询语句&#xff08;SELECT&#xff09;wherelikedistinc…

页面分页打印,echarts图解决办法;生成PDF

1&#xff1a;echarts图片前端打印不是很完美&#xff0c;对于VUE2.0版本不是很有好 2&#xff1a;360浏览器不支持vue的最新版本的插件vue3-print-nb 3&#xff1a;vue-print-nb 可以打印带有echarts 一页内容&#xff0c;并且还存在bug&#xff0c;第一次点击打印没有&…

c#学习入门1

一、环境配置 颜色主题 字体设置 行号设置 二、第一个应用程序 1. 在解决方案下创建一个新项目 第一种注释&#xff1a;两杠注释 第二种注释&#xff1a;星号注释 第三种注释&#xff1a;三杠注释(只有在花括号后面输出才会自动补全&#xff09; 2.控制台输入打印基础语句 输…

vLLM-prefix浅析(System Prompt,大模型推理加速)

原文&#xff1a;vLLM-prefix浅析&#xff08;System Prompt&#xff0c;大模型推理加速&#xff09; 简介 本文浅析了在大模型推理加速方面一个非常优秀的项目 vLLM 的一个新特性 Prefix。在 Prompt 中有相同前缀时可以提高吞吐量降低延迟&#xff0c;换句话说可以省去这部分…

Docker镜像下载

离线安装&#xff1a;物理传输 # 将镜像压缩成tar包 (将nginx 镜像打成tar包&#xff0c;然后拷贝到新机器) docker save -o xxxx.tar nginx:v1.0 # 新的机器加载 docker load -i xxx.tar # 在线安装&#xff1a;先上传到远程镜像仓库 docker tag nginx:v1.0 ldj/nginx:v1.0 #登…

【Java框架】Spring框架(四)——Spring中的Bean的创建与生命周期

目录 SpringBean的创建步骤后置处理器(PostProcessor)BeanFactoryPostProcessorBeanPostProcessorInstantiationAwareBeanPostProcessorpostProcessBeforeInstantiationpostProcessAfterInstantiationpostProcessProperties SmartInstantiationAwareBeanPostProcessordetermine…

大厂常见算法50题-反转链表

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注。 文章目录 解法参考链接题目解法一 双指针解法二 递归解法三 妖魔化的双指针总结 解法参考链接 题目 解法一 双指针 定义两个指针&#xff1a; pre 和 cur。pre 在前 cur 在后。每次让 pre的 nex…

C语言进阶课程学习记录-第48课 - 函数设计原则

C语言进阶课程学习记录 - 函数设计原则 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录

JVM学习笔记(五)内存模型

目录 1、原子性 1.1 问题分析 1.2 解决方法 2、可见性 2.1 退不出的循环 2.2 解决办法 3、有序性 3.1 诡异的结果 3.2 解决办法 3.3 有序性理解 3.4 happens-before 4、CAS与原子类 4.1 CAS 4.2 乐观锁与悲观锁 4.3 原子操作类 5、synchronized 优化 5.1 轻量…

【力扣 Hot100 | 第五天】4.20(回文链表)

1.回文链表 1.1题目 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例一&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例二&#xff1a; 输入…

深度学习入门(4)

神经网络的构建 import numpy as np import matplotlib.pyplot as plt def sigmoid(x):return 1/(1np.exp(-x)) def identity_function(x):#恒等函数return x def init_network():#进行权重和偏置的初始化&#xff0c;并保存到字典中network{}network[W1]np.array([[0.1,0.3,0…

Pytorch 学习路程 - 1:入门

目录 下载Pytorch 入门尝试 几种常见的Tensor Scalar Vector Matrix AutoGrad机制 线性回归尝试 使用hub模块 Pytorch是重要的人工智能深度学习框架。既然已经点进来&#xff0c;我们就详细的介绍一下啥是Pytorch PyTorch 希望将其代替 Numpy 来利用 GPUs 的威力&…