定时器的简单使用和实现

定时器

    • 什么是定时器
    • 标准库中的定时器
      • 使用
    • 定时器的实现

什么是定时器

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

标准库中的定时器

标准库中提供了一个Timer类, java.util.Timer

使用

Timer 类的核心方法为 schedule().

schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒).

Timer timer = new Timer();
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("3000");}
}, 3000);

这种写法很像Runnable的写法, 实际上Time类实现了Runnable接口

上述代码的意思就是在3000ms之后, 执行run()方法. 但run()方法不是在调用schedule()方法的那个线程中执行的, 而是在Timer内部的线程中执行的. 并且为了保证随时可以处理新安排的任务, 这个线程会持续执行, 并且该线程是前台线程, 此线程不结束, 那么该进程就不会结束.

定时器的实现

一个定时器里是可以有多个任务的

MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3");}
}, 3000);
timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2");}
}, 2000);
timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1");}
}, 1000);

先要能够把一个任务描述出来, 然后再用合适的数据结构将多个任务组织起来

步骤:

  1. 创建一个TimerTask这样的类, 表示一个任务, 这个任务就需要包含两方面: 任务的内容, 任务的实际执行时间.

    实际执行时间可以用时间戳表示, 在调用schedule()的时候, 先获取到当前的系统时间, 在这个基础上, 加上delay时间间隔, 得到了真实要执行这个任务的时间

  2. 使用一定的数据结构, 把多个TimerTask组织起来.

    • 如果使用List组织TimerTask的话, 如何确定此时该执行那个任务呢? 如果使用一个线程不停的对List进行遍历, 查看该任务是否到达执行时间, 就会很低效.

    • 优化: 不需要扫描所有的任务, 我们只需要关注时间最靠前的任务就行, 因为时间最早的任务还没有执行的话, 其他的任务时间也肯定还没有到. 那么如何知道那个任务时间是最靠前的呢? 此时我们就想到了一种数据结构叫做堆/优先级队列.

    • 所以使用 堆/优先级队列 来组织这些任务, 就能以最快的速度找到时间最靠前的, 也就是队首元素.

    • 针对一个任务的扫描也不必反复的一直执行, 而是在获取到队首元素的时间后, 和当前的系统时间做个差值, 根据这个差值来觉该线程休眠等待的时间, 在到达这个时间之前, 不会进行重复扫描. 这样便大大降低了扫描的次数. 并且提高了资源的利用率, 避免了不必要的cpu浪费.

  3. 提供一个扫描线程: 一方面 负责监控队首元素是否到达执行时间; 另一方面 当任务到达执行时间后, 调用run()执行任务.

  4. 注意线程安全问题: 主线程和扫描线程都会对队列操作, 要注意线程安全, 需要加锁.

//创建一个类, 描述定时器中的一个任务
class MyTimerTask implements Comparable<MyTimerTask> {//任务的具体执行时间private long time;//具体任务private Runnable runnable;/*** @param runnable 具体执行的任务* @param delay 是一个相对时间差*/public MyTimerTask (Runnable runnable, long delay) {time = System.currentTimeMillis() + delay;this.runnable = runnable;}public long getTime() {return time;}public Runnable getRunnable() {return runnable;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
}
//定时器类
class MyTimer {//锁对象private Object object = new Object();//使用优先级队列保存任务private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();/*** 定时器的核心方法, 就是把要执行的任务添加到队列中* @param runnable 要执行的任务* @param delay 相对时间差* 主线程操作queue, 需要加锁*/public void schedule(Runnable runnable, long delay) {synchronized (object) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);//每次添加新的任务后之前休眠的线程唤醒, 根据最新的任务情况, 更新最新任务的执行时间和线程休眠时间object.notify();}}/*** 扫描线程: *      一方面 负责监控队首元素是否到达执行时间,*      另一方面 当任务到达执行时间后, 调用run()执行任务.* 对queue的操作需要加锁*/public MyTimer() {//扫描线程Thread t = new Thread(() -> {while (true) {try {synchronized (object) {//只要queue是空, 那么线程开始休眠, 直到队列不再为空while (queue.isEmpty()) {object.wait();}//获取队首元素MyTimerTask myTimerTask = queue.peek();//或许当前时间long curTime = System.currentTimeMillis();//将当前时间与任务执行时间做比较, 判断是否执行if (curTime >= myTimerTask.getTime()) {queue.poll();//时间到了, 直接执行myTimerTask.getRunnable().run();} else {//时间不到, 当前线程休眠object.wait(myTimerTask.getTime() - curTime);}}} catch (InterruptedException e) {e.printStackTrace();}}});//线程开始t.start();}
}

object.wait(myTimerTask.getTime() - curTime);

object.wait();

这里为什么不用sleep()?

  1. 因为sleep()休眠的线程不会释放锁, 那么如果扫描线程正在休眠的话, 主线程调用schedule就会出现阻塞. 也不能因为线程在休眠而不能添加新的任务了吧~
  2. sleep在休眠的过程中, 不方便被提前唤醒. 因为每次添加新的任务都要把之前休眠的线程唤醒, 并且根据最新的任务情况, 更新最新任务的执行时间.

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

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

相关文章

Java GUI实现桌球小游戏

桌球游戏是一种室内运动&#xff0c;通常在一个正式的桌球台上进行。这种游戏也被称为台球或母球。桌球游戏的目标是使用一个击球杆将彩球击入桌面四个角落的袋子中&#xff0c;得分最高的一方获胜。桌球游戏需要一定的技巧和策略&#xff0c;因此是一项受欢迎的竞技运动和休闲…

pytest与unittest对比

1.unittest测试文件以test开头&#xff0c;测试方法以test开头&#xff1b;pytest测试文件以test开头&#xff0c;测试类以Test开头&#xff0c;方法以test开头 2.unittest执行&#xff0c;需要使用unittest类提供的discover方法&#xff0c;收集测试套件&#xff0c;然后通过b…

生成对抗网络Generative Adversarial Network,GAN

Basic Idea of GAN Generation&#xff08;生成器&#xff09;  Generation是一个neural network&#xff0c;它的输入是一个vector&#xff0c;它的输出是一个更高维的vector&#xff0c;以图片生成为例&#xff0c;输出就是一张图片&#xff0c;其中每个维度的值代表生…

前端环境变量释义

视频教程 彻底搞懂前端环境变量使用和原理&#xff0c;超清楚_哔哩哔哩_bilibili 添加命令行参数 --modexxxxx 新建.env.xxxx文件,其中.env文件会在所有环境下生效 以VITE_开头&#xff0c;字符串无需加双引号 使用import.meta.env.VITE_xxxxx进行调用

React中封装echarts图表组件以及自适应窗口变化

文章目录 前言环境代码接口使用效果后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react.js &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#xff0c;…

很多人都在用的现货白银突破交易法 缺点需要注意

突破交易是现货白银投资者常用的交易技巧。通常做突破交易有两种方法&#xff0c;一种是突破发生的时候马上入场&#xff0c;另一种是在突破确认后等待回调然后再入场。目前&#xff0c;投资者较多的是使用后者。用突破——回踩入场有什么优缺点呢&#xff1f;下面我们就来讨论…

GPT实战系列-P-Tuning本地化训练ChatGLM2等LLM模型,到底做了什么?(一)

GPT实战系列-如何使用P-Tuning本地化训练ChatGLM2等LLM模型&#xff1f; 文章目录 GPT实战系列-如何使用P-Tuning本地化训练ChatGLM2等LLM模型&#xff1f;P-Tuning微调训练概述1、预训练模型或者是torch模型2、训练器的超参数3、数据预处理工具4、加载数据5、分词处理6、数据预…

Java(四)(多态,final,常量,抽象类,接口)

目录 多态 基本概念: 使用多态的好处 类型转换 遇到的问题 解决方法 强制类型转换的一个注意事项 final 常量 抽象类 啥是个抽象类? 抽象类的注意事项,特点 抽象类的场景和好处 抽象类的常见应用场景: 模板方法设计模式 接口 基本概念 接口的好处 JDK8开始,接…

OpenAI宣布暂停ChatGPT plus用户订阅,解决方案,无需等待立马升级

作为人工智能领域的一项重要革新&#xff0c;ChatGPT Plus的上线引起了众多用户的关注&#xff0c;其背后的OpenAI表现出傲娇的态度&#xff0c;被誉为下一个GTP 4.0。总的来说&#xff0c;ChatGPT Plus的火爆主要有两个原因。首先&#xff0c;其在人工智能对话技术领域的创新&…

[计算机网络实验]头歌 实验二 以太网帧、IP报文分析

第1关&#xff1a;Wireshark基本使用入门 【实验目的】 1、掌握wireshark工具的基本使用方法 【实验环境】 1、头歌基于Linux的虚拟机桌面系统 2、网络报文分析工具wireshark 3、浏览器firefox 【本地主机、平台虚拟机之间数据传递】 1、文本的复制与粘贴 操作入口&…

半导体业库存问题缓解,明年迎来良好转机 | 百能云芯

随着全球半导体产业今年产值预计将出现逾1成的衰退&#xff0c;市场一度陷入不确定性。然而&#xff0c;半导体厂商们对于供应链库存的有效去化表示乐观&#xff0c;预计将为明年带来健康的复苏。在各种因素交织的复杂情况下&#xff0c;半导体产业展现出逐步解决库存问题、迎来…

vivado产生报告阅读分析14-时序报告10

Vivado IDE 中的例外报告 “ Report Exceptions ”对话框 在 AMD Vivado ™ IDE 中 &#xff0c; 选择“ Reports ” → “ Timing ” → “ Report Exceptions ” &#xff08; 报告 > 时序 > 例外报告 &#xff09; 即可打开“Report Exceptions ”对话框。 从“…

第三届VECCTF-2023 Web方向部分wp

拳拳组合 题目描述&#xff1a;明喜欢保存密钥在某个文件上。请找到秘钥并读取flag文件。 开题&#xff0c;点不完的。源码提示&#xff1a; <!--据说小明很喜欢10的幂次方--> 扫一下看看&#xff0c;应该是有git泄露。 其它一些路由没什么用 git泄露拿下一堆码 pytho…

给卖家的 5 个 TikTok 联盟营销创意

了解如何开始 TikTok 联盟营销不足以让您为 TikTok 商店实施最佳联盟计划。促进您的 TikTok 联盟营销工作。如下&#xff1a; 建立相关受众 为了确保您在 TikTok 联盟营销上的投资没有白费&#xff0c;清楚地了解您的目标受众至关重要。只有了解了这个平台的目标受众&#xf…

最全面的SHEIN开店流程,手把手教你从零起步,轻松开店!

SHEIN作为一家全球性的时尚电商平台&#xff0c;为年轻人提供了更多时尚选择和机会&#xff0c;同时也吸引了众多跨境电商卖家的关注。在5月份&#xff0c;SHEIN推出了第三方卖家平台&#xff0c;为卖家提供了全新的商机和发展赛道。毕竟目前SHEIN平台的流量是非常大的&#xf…

第2章 传输网

文章目录 2.1 传输网概述2.2 SDH传输网2.2.2 SDH的基本网络单元1、终端复用器&#xff08;TM&#xff09;2、分插复用器&#xff08;ADM&#xff09;3、再生中继器&#xff08;REG&#xff09;4、数字交叉连接设备&#xff08;DXC设备&#xff09; 2.2.3 SDH的帧结构2.2.4 …

VSCode新建Vue项目

前言 Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;它可以帮助开发者轻松构建高性能、可扩展的 Web 应用程序。而 VSCode 则是一款功能强大的开源代码编辑器&#xff0c;它提供了许多有用的工具和插件&#xff0c;可以大幅提高开发效率。 在本文中&#xff0c;我们将…

用javascript方法,禁止用户操作页面

屏蔽F12 审查元素 // 屏蔽F12 审查元素 document.onkeydown function(){if(window.event && window.event.keyCode 123) {alert("F12被禁用");event.keyCode0;event.returnValuefalse;}if(window.event && window.event.keyCode 13) {window.eve…

vivado产生报告阅读分析13-时序报告9

1、Report Exceptions 在综合后的流程中可随时使用“ Report Exceptions ” &#xff08; 例外报告 &#xff09; 命令。“ Report Exception ”命令用于报告以下信息&#xff1a; • 在设计中已置位并且影响时序分析的所有时序例外 • 在设计中已置位但由于被其他时序例外覆…