当前位置: 首页 > news >正文

【JavaEE初阶】 定时器详解与实现

文章目录

  • 🌴定时器是什么
  • 🎋Java标准库中的定时器
  • 🌲模拟实现定时器
    • 🚩定时器的构成
    • 📌第一步:MyStack类的建立
    • 📌第二步:创建MyTimer类
    • 📌第三步:解决相关问题
  • 🌳完整代码实现与测试
  • ⭕总结

🌴定时器是什么

在这里插入图片描述
定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码

定时器是一种实际开发中非常常用的组件.
比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.
比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).
类似于这样的场景就需要用到定时器.

🎋Java标准库中的定时器

  • 标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .

  • schedule 包含两个参数.

  • 第一个参数指定即将要执行的任务代码,

  • 第二个参数指定多长时间之后执行 (单位为毫秒)

代码示例:

下面程序分别有一个定时器,设置了三个不同的时间

import java.util.Timer;
import java.util.TimerTask;public class TestDemo {public static void main(String[] args) {Timer timer = new Timer();System.out.println("程序启动!");timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器3");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器1");}},1000);}
}

运行结果如下:
在这里插入图片描述
结果如我们所示,按照时间顺序进行打印

🌲模拟实现定时器

首先我们先来看一下定时器的构成

🚩定时器的构成

  1. 一个带优先级的阻塞队列
  • 为啥要带优先级呢?
    因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来.
  1. 队列中的每个元素是一个 MyTask 对象.

  2. MyTask 中带有一个时间属性, 队首元素就是即将要执行的任务

  3. 同时有一个线程一直扫描队首元素, 看队首元素是否需要执行

📌第一步:MyStack类的建立

包含两个属性

  • 包含一个 Runnable 对象

  • 一个 time(毫秒时间戳)

由于我们的MyTask类需要放入一个带优先级的阻塞队列中,所以我们需要MyTack可以比较,这里博主选择重写 Comparable 接口里的compareTo方法

代码实现如下:

public class MyTask implements Comparable<MyTask> {private Runnable runnable;private  long time;public MyTask() {System.out.println(1);}public void tad() {System.out.println(2);}public MyTask(Runnable runnable, long time) {this.runnable = runnable;this.time = time;}public long gettime(MyTask) {return this.time;}//执行任务public void run() {runnable.run();}@Overridepublic int compareTo(MyTask o) {return (int)(this.time - o.time);}
}

📌第二步:创建MyTimer类

该类需要有一个带有优先级的阻塞队列

还需要有一个可schedule 方法用于我们来插入我们我们需要执行的任务

public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);queue.put(task);}
}
  • 由于我们输入的都是一个时间大小,所以我们需要进行处理一下,

  • 这里的System.currentTimeMillis()是获取当时的时间戳

  • 再加上所需要的时间大小即达到我们效果

其次还需要一个线程循环扫描

  • 该扫描我们需要做的是

  • 取出队首元素, 检查看看队首元素任务是否到时间了.

  • 如果时间没到, 就把任务塞回队列里去.

  • 如果时间到了, 就把任务进行执行.

    private Thread t = null;public MyTimer() {t = new Thread() {@Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.MyTask myTask = queue.take();long curTime = System.currentTimeMillis();if (curTime < myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行//塞回去queue.put(myTask);} else {// 时间到了!! 执行任务!!myTask.run();}} catch (InterruptedException e) {e.printStackTrace();}}}};//启动线程t.start();}

📌第三步:解决相关问题

  • 问题一:while (true) 转的太快了, 造成了无意义的 CPU 浪费.

比如第一个任务设定的是 1 min 之后执行某个逻辑. 但是这里的 while (true) 会导致每秒钟访问队首元素几万次.

解决办法:引入一个locker对象, 借助该对象的 wait / notify 来解决 while (true) 的忙等问题.

我们在循环扫描里:引入 wait, 等待一定的时间.并修改 MyTimer 的 schedule 方法, 每次有新任务到来的时候唤醒一下循环扫描线程. (因为新插入的任务可能是需要马上执行的)

  • 问题二:原子性问题

由于我们的出队列操作和判断语句不具有原子性

问题情况如下:

出队列操作拿到任务后,还没有进行判断

然后这时候有一个来了一个新任务
在这里插入图片描述
但是此时我们该任务还没有wait()操作,而且我们由于添加新元素,notify()操作已执行,这就导致后面的wait操作不会被唤醒,那么新来的任务就在相应时间来没有被执行

解决方法:将出队列操作与判断操作都加上锁

代码实现如下:

import java.util.concurrent.PriorityBlockingQueue;public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();// 扫描线程private Thread t = null;private Object locker = new Object();public MyTimer() {t = new Thread() {@Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.synchronized (locker) {MyTask myTask = queue.take();long curTime = System.currentTimeMillis();if (curTime < myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行queue.put(myTask);// 在 put 之后, 进行一个 waitlocker.wait(myTask.getTime() - curTime);} else {// 时间到了!! 执行任务!!myTask.run();}}} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();}// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);queue.put(task);synchronized (locker) {locker.notify();}}
}

🌳完整代码实现与测试

计时器完整代码:

import java.util.concurrent.PriorityBlockingQueue;class MyTask implements Comparable<MyTask> {private Runnable runnable;private  long time;public MyTask() {System.out.println(1);}public void tad() {System.out.println(2);}public MyTask(Runnable runnable, long time) {this.runnable = runnable;this.time = time;}public long getTime() {return this.time;}//执行任务public void run() {runnable.run();}@Overridepublic int compareTo(MyTask o) {return (int)(this.time - o.time);}
}public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();// 扫描线程private Thread t = null;private Object locker = new Object();public MyTimer() {t = new Thread() {@Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.synchronized (locker) {MyTask myTask = queue.take();long curTime = System.currentTimeMillis();if (curTime < myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行queue.put(myTask);// 在 put 之后, 进行一个 waitlocker.wait(myTask.getTime() - curTime);} else {// 时间到了!! 执行任务!!myTask.run();}}} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();}// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);queue.put(task);synchronized (locker) {locker.notify();}}
}

测试代码如下

public class TestDemo2 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();System.out.println("程序启动");myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("计时器3");}},3000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("计时器2");}},2000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("计时器1");}},1000);}
}

测试结果如下:
在这里插入图片描述

⭕总结

关于《【JavaEE初阶】 定时器详解与实现》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

相关文章:

【JavaEE初阶】 定时器详解与实现

文章目录 &#x1f334;定时器是什么&#x1f38b;Java标准库中的定时器&#x1f332;模拟实现定时器&#x1f6a9;定时器的构成&#x1f4cc;第一步&#xff1a;MyStack类的建立&#x1f4cc;第二步&#xff1a;创建MyTimer类&#x1f4cc;第三步&#xff1a;解决相关问题 &am…...

基于YOLOv8模型和WiderPerson数据集的行人目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型和WiderPerson数据集的行人目标检测系统可用于日常生活中检测与定位行人目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标…...

COSCon'23 开源社文创丨 给开源人一点“color see see”

成都城市限定 “小O在成都”行李箱贴纸 成都限定行李箱贴纸把小O和特色元素相融合 当小O遇到成都 在云端漫步的蓝色小章鱼 掉落到这座热情似火的城市&#xff0c; 结识了大熊猫朋友 学会了四川麻将 吃到了红油串串... 快带着小O来一场自由的旅游吧&#xff01; “你也要尝尝竹子…...

C++前缀和算法的应用:从仓库到码头运输箱子原理、源码、测试用例

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 双指针 单调双向队列 题目 你有一辆货运卡车&#xff0c;你需要用这一辆车把一些箱子从仓库运送到码头。这辆卡车每次运输有 箱子数目的限制 和 总重量的限制 。 给你…...

【面试HOT100】链表树

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于LeetCodeHot100进行的&#xff0c;每个知识点的修正和深入主要参考…...

了解 Elasticsearch 自动生成的文档 _id:重复是一个问题吗?

Elasticsearch 中自动生成的文档 ID 当你在未指定 ID 的情况下对文档建立索引时&#xff0c;Elasticsearch 会自动为该文档生成唯一的 ID。 该 ID 是 Base64 编码的 UUID&#xff0c;由多个部分组成&#xff0c;每个部分都有特定的用途。 ID 生成过程针对索引速度和存储效率进…...

量子信息处理器可能能够提供高度压缩的生成对抗学习任务的版本

量子信息处理在生成对抗学习任务中的应用可能性&#xff0c;以及量子信息处理器在表示高维向量和执行线性代数运算上的优势。 举个例子 假设底层数据由M个在N维实数或复数空间中的归一化向量~vj组成&#xff0c;使得数据的&#xff08;归一化&#xff09;协方差矩阵为C (1/M…...

linux-守护进程daemon

linux-守护进程daemon 代码实现 main.c运行结果 代码实现 main.c //pName&#xff1a;程序名 //facility&#xff1a; 守护进程&#xff0c;输出日志类型 302页 #include<signal.h> #include<syslog.h> #include<fcntl.h> static int daemon_proc 0; #defin…...

Kafka Tool(Kafka 可视化工具)安装及使用教程

Kafka Tool&#xff08;Kafka 可视化工具&#xff09;安装及使用教程 Kafka Tool 工具下载 下载地址 http://www.kafkatool.com/download.html 下载界面 不同版本的Kafka对应不同版本的工具&#xff0c;个人使用的是2.11&#xff0c;所以下载的是最新的2.0.8版本&#xff…...

【大揭秘】美团面试题:ConcurrentHashMap和Hashtable有什么区别?一文解析!

正文 亲爱的小伙伴们&#xff0c;大家好&#xff01;我是小米&#xff0c;一个热爱技术分享的程序员&#xff0c;今天我为大家带来了一篇有关美团面试题的热门话题&#xff1a;ConcurrentHashMap 和 Hashtable 有什么区别。这个问题在Java面试中常常被拿来考察对多线程编程的理…...

爬虫基础 JS逆向

爬虫核心 1. HTTP协议与WEB开发 1. 什么是请求头请求体&#xff0c;响应头响应体 2. URL地址包括什么 3. get请求和post请求到底是什么 4. Content-Type是什么 &#xff08;1&#xff09;简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;…...

nextTick实现原理

答题思路&#xff1a; 此题实际考查vue异步更新策略说出vue是怎么通过异步、批量的方式更新以提高性能的最后把源码中实现说一下 回答范例&#xff1a; vue有个批量、异步更新策略&#xff0c;数据变化时&#xff0c;vue开启一个队列&#xff0c;并缓冲在同一事件循环中发生的…...

CentOS 7中安装ZooKeeper

文章目录 下载解压安装环境变量配置文件启动设置开机自启动开放端口 CentOS 7.6 ZooKeeper 3.5.7 本文介绍了如何在CentOS 7系统中安装单机版的ZooKeeper。 下载 点击官网下载 解压安装 # 解压 tar -xzvf apache-zookeeper-3.5.7-bin.tar.gz sudo mv apache-zookeeper-3.5.…...

推荐《幽游白书》

《幽游白书》是日本漫画家富坚义博于1990年12月3日—1994年7月25日于集英社旗下杂志《周刊少年Jump》上连载的少年漫画作品&#xff0c;全175话&#xff08;含外传一话&#xff09;。现时发行的单行本共计19册&#xff0c;电子版由漫番漫画、哔哩哔哩漫画发布 [1-2] 。 本作最…...

Linux MMC子系统 - 1.eMMC简介

By: Ailson Jack Date: 2023.10.21 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/160.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…...

聊聊Android线程优化这件事

一、背景 在日常开发APP的过程中&#xff0c;难免需要使用第二方库和第三方库来帮助开发者快速实现一些功能&#xff0c;提高开发效率。但是&#xff0c;这些库也可能会给线程带来一定的压力&#xff0c;主要表现在以下几个方面&#xff1a; 线程数量增多&#xff1a;一些库可…...

Linux性能优化--实用工具:性能工具助手

8.0 概述 本章介绍一些在Linux系统上可用的实用程序&#xff0c;它们能够加强性能工具的有效性和可用性。实用工具本身不是性能工具&#xff0c;但是当它们与性能工具一起使用时&#xff0c;它们可以帮助完成如下功能&#xff1a;自动执行繁琐的任务、分析性能统计数据&#x…...

[PyTorch]即插即用的热力图生成

先上张效果图&#xff0c;本来打算移植霹雳老师的使用Pytorch实现Grad-CAM并绘制热力图。但是看了下代码&#xff0c;需要骨干网络按照标准写法&#xff08;即将特征层封装为features数组&#xff09;&#xff0c;而我写的网络图省事并没有进行封装&#xff0c;改造网络的代价又…...

golang笔记18--go并发多线程

golang笔记18--go并发多线程 介绍核心用法MutexRWMutexWaitGroupCondOncemapPoolContextselect 注意事项参考文档 介绍 大家都知道go语言近年来越来越火了&#xff0c;其中有一个要点是go语言在并发场景有很高的性能&#xff0c;比如可以通过启动很多个 goroutine 来执行并发任…...

使用OkHttp和Java来下载

以下是一个使用OkHttp和Java来下载内容的下载器程序&#xff0c;同时使用了jshk.com.cn/get_proxy来获取代理服务器。请注意&#xff0c;为了简化代码&#xff0c;我们将忽略一些异常处理和安全性检查。 import java.io.File;import java.io.FileOutputStream;import java.io.I…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...