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.rhkb.cn/news/314913.html

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

相关文章

JavaScript云LIS系统源码 前端框架JQuery+EasyUI+后端框架MVC+SQLSuga大型医院云LIS检验系统源码 可直接上项目

JavaScript云LIS系统源码 前端框架JQueryEasyUI后端框架MVCSQLSuga大型医院云LIS检验系统源码 可直接上项目 云LIS系统概述&#xff1a; 云LIS是为区域医疗提供临床实验室信息服务的计算机应用程序&#xff0c;可协助区域内所有临床实验室相互协调并完成日常检验工作&#xff…

cve-2018-19518漏洞复现

一、靶场的启动 在相应的文件夹位置打开终端后进行如下操作 1.运行此靶场 sudo docker-compose up -d 2.查看启动环境 sudo docker ps 3.关闭此靶场环境 docker-compose down 二、漏洞内容简介 php imap扩展用户在php中执行邮件收发操作&#xff0c;其imap_open函数会调用rsh…

【java数据结构之八大排序(上)-直接插入排序,希尔排序,选择排序,堆排序,向下调整(大根堆,小根堆)等知识详解】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

探索开源的容器引擎--------------Docker容器操作

目录 一、Docker 容器操作 1.1容器创建 1.2查看容器的运行状态 1.3启动容器 1.4创建并启动容器 1.4.1当利用 docker run 来创建容器时&#xff0c; Docker 在后台的标准运行过程是&#xff1a; 1.4.2在后台持续运行 docker run 创建的容器 1.4.3创建容器并持续运行容器…

(超级详细)算法刷题Leecode15. 三数之和

题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组…

晶圆制造之MPW(多项目晶圆)简介

01、MPW是什么&#xff1f; 在半导体行业中&#xff0c;MPW 是 "Multi Project Wafer" 的缩写&#xff0c;中文意思是多项目晶圆。MPW 的主要思想是将使用相同工艺的多个集成电路设计放在同一晶圆片上进行流片&#xff08;即制造&#xff09;。这种方法允许多个设计共…

2024全新瀚海跑道:矢量图片迅速养号游戏玩法,每天一小时,日转现200

最初我注意到这种玩法&#xff0c;是因为最近在浏览各大平台的视频时&#xff0c;我发现了一种特殊类型的账号&#xff0c;其养号成功率高达90%。这些账号发布的视频内容和数据非常夸张&#xff0c;而且制作起来非常简单&#xff0c;任何人都可以轻松上手。这些账号主要发布矢量…

python爬虫 - 爬取html中的script数据(zum.com新闻信息 )

文章目录 1. 分析页面内容数据格式2. 使用re.findall方法&#xff0c;编写爬虫代码3. 使用re.search 方法&#xff0c;编写爬虫代码 1. 分析页面内容数据格式 &#xff08;1&#xff09;打开 https://zum.com/ &#xff08;2&#xff09;按F12&#xff08;或 在网页上右键 --…

模型部署的艺术:让深度学习模型跃入生产现实

模型部署的艺术&#xff1a;让深度学习模型跃入生产现实 1 引言 1.1 部署的意义&#xff1a;为何部署是项目成功的关键 在深度学习项目的生命周期中&#xff0c;模型的部署是其成败的关键之一。通常&#xff0c;一个模型从概念构思、数据收集、训练到优化&#xff0c;最终目的…

Atcoder Beginner Contest351 A-E Solution题解

文章目录 [A - The bottom of the ninth](https://atcoder.jp/contests/abc351/tasks/abc351_a)[B - Spot the Difference ](https://atcoder.jp/contests/abc351/tasks/abc351_b)[D - Grid and Magnet](https://atcoder.jp/contests/abc351/tasks/abc351_d)E Note&#xff1a;…

Blender笔记之基本操作

code review! —— 2024-04-27 杭州 Blender笔记…

自动驾驶传感器篇: GNSSIMU组合导航

自动驾驶传感器篇&#xff1a; GNSS&IMU组合导航 1.GNSS1.1 GNSS 系统概述1.2 GNSS系统基本组成1. 空间部分&#xff08;Space Segment&#xff09;&#xff1a;2. 地面控制部分&#xff08;Ground Control Segment&#xff09;&#xff1a;3. 用户设备部分&#xff08;Use…

Docker数据管理、网络通信和Dockerfile

一.数据管理 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻可见&#xff0c;并且更新数据不会影响镜像&#xff0c;从而实现数据在宿主机与容器之间的迁移。数据卷的使用类似于 Linux 下对目录…

eclipse导入工程提示Project has no explicit encoding set

eclipse导入工程提示Project has no explicit encoding set 文章目录 eclipse导入工程提示Project has no explicit encoding set一、Eclipse的工程导入二、可能的问题1.在工程名下有黄色叹号 一、Eclipse的工程导入 用Eclipse的导入可以将原有工程导入到新环境中 具体方法是&…

自动驾驶框架 UniAD环境部署

感谢大佬们的开源工作 UniAD-github地址-YYDS更多bev算法部署参考如果您觉得本帖对您有帮助&#xff0c;感谢您一键三连支持一波^_^ 统一自动驾驶框架 (UniAD) &#xff0c;第一个将全栈驾驶任务整合到一个深度神经网络中的框架&#xff0c;并可以发挥每个子任务以及各个模块的…

牛客NC195 二叉树的直径【simple DFS C++ / Java /Go/ PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/15f977cedc5a4ffa8f03a3433d18650d 思路 最长路径有两种情况&#xff1a; 1.最长条路径经过根节点&#xff0c;那么只需要找出根节点的左右两棵子树的最大深度然后相加即可。 2.最长路径没有经过根节点&#xf…

基于Spring Boot的火车订票管理系统设计与实现

基于Spring Boot的火车订票管理系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;在系统首页可以查看…

[ESP32]:TFLite Micro推理CIFAR10模型

[ESP32]&#xff1a;TFLite Micro推理CIFAR10模型 模型训练 数据集处理 from keras.datasets import cifar10 from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, load_model, Model from keras.layers import Input, Dense, …

根据标签最大层面ROI提取原始图像区域

今天要实现的任务是提取肿瘤的感兴趣区域。 有两个文件&#xff0c;一个是nii的原始图像文件&#xff0c;一个是nii的标签文件。 我们要实现的是&#xff1a;在标签文件上选出最大层面&#xff0c;然后把最大层面的ROI映射到原始图像区域&#xff0c;在原始图像上提裁剪出ROI…

Swift - 函数

文章目录 Swift - 函数1. 函数的定义2. 隐式返回(Implicit Return)3. 返回元组&#xff1a;实现多返回值4. 函数的文档注释5. 参数标签&#xff08;Argument Label&#xff09;6. 默认参数值&#xff08;Default Parameter Value&#xff09;7. 可变参数&#xff08;Variadic P…