当前位置: 首页 > 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…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...