「JavaEE」多线程案例分析2:实现定时器

🎇个人主页:Ice_Sugar_7
🎇所属专栏:JavaEE
🎇欢迎点赞收藏加关注哦!

实现定时器

  • 🍉简介
  • 🍉模拟实现定时器

🍉简介

定时器类似一个闹钟,时间到了之后就会执行相应的任务
Java 标准库中已经实现了一个定时器的类 Timer

Timer timer = new Timer();

在定义好 timer 之后可以调用 schedule 把一个或多个任务(TimerTask)添加到定时器中

timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000 ms");}
},2000);

第一个参数就是任务内容,每个任务后面都会带有一个时间(第二个参数),这个时间是“相对时间”,是以 schedule 时的时间为基准,过了相对时间后才执行
比如 2000ms,它表示调用 schedule 后再过 2000ms 就会执行这个任务

TimerTask 里面有一个 run 方法,而 run 是线程的入口,说明 timer 创建了一个线程来执行任务。这个线程是前台线程,它会阻止主线程结束,需要我们使用 cancel 主动结束,否则 Timer 不知道其他地方是否会继续添加任务

public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000 ms");timer.cancel(); //结束线程}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1000 ms");}},1000);
}

在这里插入图片描述


🍉模拟实现定时器

首先要有一个数据结构负责保存 schedule 的任务(相当于任务清单),因为我们是先执行时间近的任务(比如有两个任务,一个是两点执行,另一个是两点半执行,肯定要先完成前者),换而言之,任务之间是有优先级的,所以要用优先级队列
标准库中提供了 PriorityQueue 和 PriorityBlockingQueue,前者是线程不安全的,后者是线程安全的,在此处的场景中 PriorityBlockingQueue 不太好控制,容易出问题,所以我们用前者

(补充:TreeSet 和 TreeMap 虽然也是有序的,但是获取到最小值的时间复杂度为 O(logN),不及 O(1) 的优先级队列)

然后需要有一个线程不断扫描优先级队列的队首元素,看它时间到了没

public class MyTimer {private Thread t;private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer(){//定时器构造方法的主体就是启动线程,让它去扫描队首元素t = new Thread(() -> {while (true) {synchronized (locker) {while (queue.isEmpty()) {try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime >= task.getTime()) { //时间到了,执行任务task.run();queue.poll(); //记得执行后把它出队列} else {try {locker.wait(task.getTime() - curTime); //如果还没到执行时间,那就等待,不要一直循环下去} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});t.start();}public void schedule(Runnable runnable,long delay) { //把任务添加到 queue 里面synchronized (locker) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);locker.notify(); //添加元素后,就可以唤醒处于 wait 状态的线程}}
}

队列的元素——任务,它是一个类。它的成员变量应该包括时间、能让它跑起来的 Runnable 接口

public class MyTimerTask{private long time; //执行任务的时间(注意这个是“绝对时间”)private Runnable runnable; //持有 Runnable 接口可以调用它的 run 方法,也可以不持有 Runnable,而是实现 Runnable 接口并重写 run 方法MyTimerTask(Runnable runnable,long time) {this.runnable = runnable;this.time = time + System.currentTimeMillis(); //什么时候执行任务:现在的时间 + 相对时间}public void run() {runnable.run();}
}

因为优先级队列要求元素是可排序的,所以我们需要实现 Comparable 接口并重写 compareTo 方法

public class MyTimerTask implements Comparable<MyTimerTask>{private long time; //执行任务的时间(注意这个是“绝对时间”)private Runnable runnable; //持有 Runnable 接口可以调用它的 run 方法,也可以不持有 Runnable,而是实现 Runnable 接口并重写 run 方法MyTimerTask(Runnable runnable,long time) {this.runnable = runnable;this.time = time + System.currentTimeMillis(); //什么时候执行任务:现在的时间 + 相对时间}public void run() {runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int) (o.time-this.time); //时间小的优先级更高}
}

补充:compareTo 方法里面是 o.time-this.time 还是 this.time - o.time,不用去刻意记忆,两种都试一下就 ok 了

测试一下:

public class TestDemo {public static void main(String[] args) throws InterruptedException {MyTimer myTimer = new MyTimer();myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000 ms");}},3000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000 ms");}},1000);}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/698294.html

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

相关文章

java 解决跨域时遇到问题,怎么来做一个跨域环境

今天遇到一个问题&#xff1a; 关于#java#的问题&#xff1a;java 解决跨域时遇到问题&#xff1a;为什么跨域访问时配置的CorsFilter没有进入&#xff1f;直接访问请求地址时进入了配置的CorsFilter 由于没有实际的跨域环境&#xff0c;因此打算在本机建一个跨域环境&#xff…

Java 实现Mybatis plus 批量删除

数据库实体字段并不映射的情况&#xff0c;直接请求体集合接收。 PostMapping("/removeIdsInfo")public R<Void> removeIdsInfo(RequestBody List<Integer> ids) {return exStudentService.removeIdsInfo(ids);} /**** 学生模块根据集合id 批量删除数据*…

深度解析Nginx:高性能Web服务器的奥秘(下)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《洞察之眼&#xff1a;ELK监控与可视化》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、Nginx概述 二、Nginx核心功能 1、URL重写与重…

Springboot整合 Spring Cloud Gateway

1.Gateway介绍 1.是spring cloud官方推出的响应式的API网关框架&#xff0c;旨在为微服务架构提供一种简单有效的API路由的管理方式&#xff0c;并基于Filter的方式提供网关的基本功能&#xff0c;例如&#xff1a;安全认证&#xff0c;监控&#xff0c;限流等等。 2.功能特征…

为什么很多计算机专业的同学毕业即失业❓

✅大部分计算机专业毕业生在就业时遇到困难&#xff0c;原因往往是多方面的&#xff0c;并非普遍情况&#xff0c;主要包括以下几点&#xff1a; 1.技能不匹配&#xff1a;学校所学知识可能与实际工作需求有一定差距&#xff0c;比如缺乏特定编程语言的深入掌握或实际项目经验。…

基于MetaGPT的LLM Agent学习实战(一)

前言 我最近一直在做基于AI Agent 的个人项目&#xff0c; 因为工作加班较多&#xff0c;设计思考时间不足&#xff0c;这里借着Datawhale的开源学习课程《MetaGPT智能体理论与实战》课程&#xff0c;来完善自己的思路&#xff0c;抛砖引玉&#xff0c;和各位开发者一起学习&am…

MySQL 8.4参考手册

5.1 连接到服务器和断开服务器连接 host 和 user 表示主机名&#xff0c;其中 MySQL服务器正在运行&#xff0c;并且您的MySQL帐户的用户名。 为您的设置替换适当的值。代表您的密码;输入它 当 MySQL 显示提示时。********Enter password: 5.2 输入查询 mysql> SELECT VERSI…

美国分析人工智能技术对网络格局的影响(中)

文章目录 前言三、剑更锋利,盾更坚固四、各国对于储存与披露已发现的漏洞有不同的国家级法规五、人工智能将如何改变网络安全的问题既是一个技术问题,也是一个政策问题前言 美国政府正在探索如何使用人工智能来增强其网络能力,如何使用人工智能来加强网络防御,以及如何最好…

照明灯具十大排名都有哪些?市面上比较流行的十大护眼台灯品牌推荐

照明灯具十大排名都有哪些&#xff1f;护眼台灯排名当中靠前的主要有书客、飞利浦、松下等品牌。照明灯具作为家居与办公环境中不可或缺的元素&#xff0c;其品质与选择直接关系到人们的视觉健康与舒适度。本文将为大家揭示照明灯具的十大排名&#xff0c;让大家了解市场上最受…

[QNX] BSP 网络性能优化:调优io-pkt和ClockPeriod提升网速

0 概要 本文介绍如何在QNX系统上优化网络性能&#xff0c;主要通过调整io-pkt和ClockPeriod参数来实现。通过优化&#xff0c;网络吞吐量可以得到显著提升。 1 优化方法 1.1 调整io-pkt的mclbytes参数: io-pkt是QNX系统中常用的网络协议栈&#xff0c;其mclbytes参数指定了…

HTML的使用(上)

文章目录 前言一、HTML是什么&#xff1f;二、使用内容 &#xff08;1&#xff09;换行标记<br>&#xff08;2&#xff09;加粗标记<b> </b>&#xff08;3&#xff09;段落标记<p> </p>&#xff08;4&#xff09;标题标记<h1>~<h6> …

韵搜坊(全栈)-- 前后端初始化

文章目录 前端初始化后端初始化 前端初始化 使用ant design of vue 组件库 官网快速上手&#xff1a;https://www.antdv.com/docs/vue/getting-started-cn 安装脚手架工具 进入cmd $ npm install -g vue/cli # OR $ yarn global add vue/cli创建一个项目 $ vue create ant…