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

模拟实现定时器

关于java标准库中的定时器的使用可以看定时器Timer的使用

大致思路

        定义一个MyTimeTask类,该类用于组织要执行任务的内容以及执行任务的时间戳,后面要根据当前系统时间以及执行任务的时间戳进行比较,来判断是否要执行任务或是要等待任务

        用一个MyTimer类作为定时器的主体,在MyTimer类中用小根堆这个数据结构来存储要执行的任务

        在MyTimer类中定义一个schedule方法,通过传入的参数来实例化一个任务,再将这个任务插入到小根堆中

        在MyTimer类中创建一个扫描线程,一方面监控堆首元素是否到点了,一方面在到点后,要调用Runable中的run方法来执行任务,因为在创建MyTimer对象时就应该开始进行对任务的扫描,所以,扫描线程应该定义在MyTimer类的构造方法中

为什么要用小根堆来存储待执行的任务呢?

        因为小根堆的堆顶是等待时间最短的任务,在众多任务中,我们只需要关注最早要进行的那个任务就可以了,要是最早要进行的任务都还不能执行,其他任务肯定都不能执行,如果我们不用小根堆的话,用链表的话,我们想要知道接下来要执行的任务就需要一直不停的遍历链表中的任务,将执行任务的时间戳与当前时间进行比较,一直不停的遍历代表一直占用cpu资源,这是不合理的。所以用小根堆来存储待执行的任务呢是比较合理的

MyTimeTask类的创建

        代码展示

//描述要执行任务的内容,以及执行任务的时间戳
class MyTimeTask implements Comparable<MyTimeTask>{//由于该类的对象要存放到小根堆中,所以要实现Comparable接口,重写compareTo方法// 才可以给出合适的比较规则,才可以放入堆中@Overridepublic int compareTo(MyTimeTask o) {return (int)(this.time-o.time);}//要执行任务的内容private Runnable runnable;//执行任务的时间戳private long time;//构造函数传入要执行的任务以及等待的时间//delay是一个时间差,类似于3000这样的数值public MyTimeTask(Runnable runnable,long delay){this.runnable=runnable;time=System.currentTimeMillis()+delay;  //当前系统时间加上延迟的时间就是任务要执行的时间戳}public Runnable getRunnable(){return runnable;}public long getTime(){return time;}
}

        注意事项

        1.由于MyTimeTask类中的time成员属性代表的是任务执行的时间戳,但是用户输入的参数delay是任务执行的延迟时间,所以任务执行的时间戳是当前系统时间加上延迟的时间

        2.由于MyTimeTask类要添加到小根堆中,所以要先在MyTimeTask类中定义好放到堆中的优先级关系,这里采用MyTimeTask类实现Comparable接口,重写compareTo方法这个方法来规定MyTimeTask类的优先级关系

MyTimer类的创建

        代码展示

/定时器类的主体
class MyTimer{//用小根堆这个数据结构来存储要执行的任务//因为小根堆堆顶的数是延迟时间最短,最快要执行的任务PriorityQueue<MyTimeTask> queue=new PriorityQueue<>();Object locker=new Object(); //用来加锁的锁对象//定时器的核心方法,将任务添加到堆中//涉及到多线程,可能有多个线程调用schedule添加任务到堆中,而且MyTimer内部的线程也要对堆进行修改//所以存在线程安全问题,并且schedule和扫描线程应该对同一个对象进行加锁public void schedule(Runnable runnable,long delay){synchronized(locker){MyTimeTask task=new MyTimeTask(runnable,delay);queue.offer(task);locker.notify();}}//定义一个扫描线程,一方面监控堆首元素是否到点了,一方面在到点后,要调用Runable中的run方法来执行任务//扫描线程不应该是用户自己调用的,而是一创建MyTimer对象就要有扫描线程来判断是否有需要执行的任务,所以应该写在构造方法中public MyTimer(){Thread t=new Thread(()->{synchronized(locker){   //因为接下来的代码中都涉及到对堆的操作,所以直接整体加上锁while (true){//wait推荐和while搭配使用,因为wait可能是不是被notify正常唤醒的,所以被唤醒了以后条件可能依然不满足// 所以要用while循环对条件是否满足进行多次判断while (queue.isEmpty()){//continue;try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//取出任务判断是否需要执行,不删除MyTimeTask task=queue.peek();if(task.getTime()<=System.currentTimeMillis()){task.getRunnable().run();   //时间到了要执行任务queue.poll();   //执行任务后将任务从堆中弹出}else {//任务时间还没有到不需要执行,进行睡眠,等时间到了再执行try {//sleep不适合在这里用于进行休眠//1.sleep休眠不会释放锁,就代表在进行休眠的时候虽然线程没有对堆进行操作// 但locker对象还是被锁着的,此时要往队列中添加数据也要进行等待,这是不合理的//2.如果我们新增了一个任务,可能这条任务的执行时间比当前堆顶最近的执行时间还要早进行//此时我们就需要从睡眠状态脱离,去检查是否要更改随眠时间,但sleep睡眠的过程中不方便提前中断//虽然可以用interrupt提前中断,但是使用interrupt意味着程序已经要结束了//所以使用wait来进行休眠是比sleep更加合理的//Thread.sleep(task.getTime()-System.currentTimeMillis());locker.wait(task.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});//线程定义完毕,启动线程t.start();}}

        注意事项

        1.定义一个schedule方法,用户通过传入任务执行内容以及延迟时间来使用该方法,在schedule中,通过用户传入的参数来实例化一个MyTimeTask的任务,将该任务添加到小根堆中。

        2.要进行执行任务的话要在MyTimer内部的扫描线程中执行,由于MyTimer类实例化以后就需要判断是否有新的任务需要处理,所以扫描线程应该创建在MyTimer类的构造方法中,通过循环扫描堆顶的的任务,当堆为空的时候就进入wait睡眠直到用户调用schedule方法向堆中传入任务的时候才调用notify解除睡眠,关于wait和notify可以看通过wait和notify来协调线程执行顺序

        当堆不为空的时候,就要取出堆顶的任务根据执行任务的时间戳以及当期系统时间来判断是否需要执行,不删除,要是当前系统时间以及>=任务时间的话,就需要调用task.getRunnable().run()来执行任务,要是当前系统时间以及<任务时间就需要进行wait睡眠,睡眠的时间便是任务执行的时间戳-当前系统时间,因为当前所扫描的这个任务便是最早执行的了,所以其他任务没有扫描的必要,它们更完执行

        3.为什么要schedule方法和扫描线程加上synchronized锁呢(关于synchronized锁可以看线程安全问题),因为schedule方法涉及到向堆中添加数据,要是多个线程调用schedule方法就会出现线程安全问题,而扫描线程中也有多次涉及到堆的使用,所以也同样具有线程安全问题,所以要加上synchronized锁来保证线程安全,并且因为schedule方法和扫描线程都是对堆进行修改,所以它们要对同一个对象加锁。

        4.sleep同样也可以1进行睡眠,为什么我们要用wait,notify呢

                (1).sleep休眠不会释放锁,就代表在进行休眠的时候虽然线程没有对堆进行操作, 但locker对象还是被锁着的,此时要往队列中添加数据也要进行等待,这是不合理的

                (2).如果我们新增了一个任务,可能这条任务的执行时间比当前堆顶最近的执行时间还要早进行,此时我们就需要从睡眠状态脱离,去检查是否要更改随眠时间,但sleep睡眠的过程中不方便提前中断,虽然可以用interrupt提前中断,但是使用interrupt意味着程序已经要结束了

        所以使用wait来进行休眠是比sleep更加合理的

        

相关文章:

模拟实现定时器

关于java标准库中的定时器的使用可以看定时器Timer的使用 大致思路 定义一个MyTimeTask类&#xff0c;该类用于组织要执行任务的内容以及执行任务的时间戳&#xff0c;后面要根据当前系统时间以及执行任务的时间戳进行比较&#xff0c;来判断是否要执行任务或是要等待任务 用一…...

TCP/IP的分包粘包

TCP/IP的分包粘包 分包粘包介绍导致分包粘包的原因导致TCP粘包的原因&#xff1a;导致TCP分包的原因&#xff1a;避免分包粘包的措施 分包粘包介绍 因为TCP为了减少额外开销&#xff0c;采取的是流式传输&#xff0c;所以接收端在一次接收的时候有可能一次接收多个包。而TCP粘…...

盘点:查快递教程

在“寄快递”成为常态的当下&#xff0c;如何快速进行物流信息查询&#xff0c;是收寄人所关心的问题。在回答这个问题之前&#xff0c;首先我们要知道&#xff0c;物流信息查询&#xff0c;有哪些方法&#xff1f; 1、官网单号查询 知道物流公司和单号的情况下&#xff0c;直…...

TransGPT 开源交通大模型开源

TransGPT 是开源交通大模型&#xff0c;主要致力于在真实交通行业中发挥实际价值。 它能够实现交通情况预测、智能咨询助手、公共交通服务、交通规划设计、交通安全教育、协助管理、交通事故报告和分析、自动驾驶辅助系统等功能。 TransGPT 作为一个通用常识交通大模型&#…...

gitignore文件使用方法(gitignore教程)(git status --ignored)(git check-ignore -v <file>)

文章目录 Gitignore文件使用描述Gitignore基本语法1. 基本语法★★★★★2. 配置方法 匹配示例示例1示例2示例3 其他命令git status --ignored&#xff08;用于显示被Git忽略的文件和文件夹的状态&#xff09;git check-ignore -v <file>&#xff08;用于检查指定文件是否…...

mybatis拼接sql导致的oom报错 GC报错

报错1&#xff1a;mybatis拼接过多 java.lang.OutOfMemoryError: GC overhead limit exceeded 具体报错&#xff1a; nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression ew.sqlSegment ! null and ew.sqlSegment ! and ew.non…...

如何通俗理解扩散模型?

扩散模型(Diffusion Model)是一类十分先进的基于扩散思想的深度学习生 成模型。生成模型除了扩散模型之外&#xff0c;还有出现较早的 VAE ( Variational Auto- Encoder&#xff0c;变分自编码器) 和 GAN ( Generative Adversarial Net &#xff0c;生成对抗网络) 等。 虽然它们…...

【C#】并行编程实战:并行编程中的模式

本章将介绍并行编程模式&#xff0c;重点是理解并行代码问题场景并使用并行编程/异步技术解决他们。本章会介绍几种最重要的编程模式。 本教程学习工程&#xff1a;魔术师Dix / HandsOnParallelProgramming GitCode 1、MapReduce 模式 引入 MapReduce 是为了解决处理大数据的问…...

Apache Kafka 入门教程

Apache Kafka 入门教程 一、简介简介架构 二、Kafka 安装和配置JDK安装 Kafka配置文件详解 三、Kafka 的基本操作启动和关闭Topic 创建和删除Partitions 和 Replication 配置Producer 和 Consumer 使用方法ProducerConsumer 四、Kafka 高级应用消息的可靠性保证Kafka StreamKaf…...

python皮卡丘编程代码教程,用python打印皮卡丘

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;如何用print函数打印一只皮卡丘&#xff0c;用python如何打印丘比特之心&#xff0c;现在让我们一起来看看吧&#xff01;...

shell脚本:数据库的分库分表

#!/bin/bash ######################### #File name:db_fen.sh #Version:v1.0 #Email:admintest.com #Created time:2023-07-29 09:18:52 #Description: ########################## MySQL连接信息 db_user"root" db_password"RedHat123" db_cmd"-u${…...

AtCoder Beginner Contest 312(A~D)

A //语法题也要更仔细嘞&#xff0c;要不然也会wa #include <bits/stdc.h> // #pragma GCC optimize(3,"Ofast","inline") // #pragma GCC optimize(2) using namespace std; typedef long long LL; #define int LL typedef pair<int, int> …...

SQL中Partition的相关用法

使用Partition可以根据指定的列或表达式将数据分成多个分区。每个分区都是逻辑上独立的&#xff0c;可以单独进行查询、插入、更新和删除操作。Partition可以提高查询性能&#xff0c;因为它可以限制在特定分区上执行查询&#xff0c;而不是在整个表上执行。 在SQL中&#xff…...

微服务——Docker

docker与虚拟机的区别 首先要知道三个层次 硬件层:计算机硬件 内核层:与硬件交互&#xff0c;提供操作硬件的指令 应用层: 系统应用封装内核指令为函数&#xff0c;便于程序员调用。用户程序基于系统函数库实现功能。 docker在打包的时候直接把应用层的函数库也进行打包&a…...

测试|测试用例方法篇

测试|测试用例方法篇 文章目录 测试|测试用例方法篇1.测试用例的基本要素&#xff1a;测试环境&#xff0c;操作步骤&#xff0c;测试数据&#xff0c;预期结果…2.测试用例带来的好处3.测试用例的设计思路&#xff0c;设计方法&#xff0c;具体设计方法之间的关系**设计测试用…...

负载均衡的策略有哪些? 负载均衡的三种方式?

负载均衡的策略有哪些? 负载均衡的策略有如下&#xff1a; 1. 轮询&#xff08;Round Robin&#xff09;&#xff1a;按照请求的顺序轮流分配到不同的服务器。 2. 权重&#xff08;Weighted&#xff09;&#xff1a;给不同的服务器分配不同的权重&#xff0c;根据权重比例来…...

二十三章:抗对抗性操纵的弱监督和半监督语义分割的属性解释

0.摘要 弱监督语义分割从分类器中生成像素级定位&#xff0c;但往往会限制其关注目标对象的一个小的区域。AdvCAM是一种图像的属性图&#xff0c;通过增加分类分数来进行操作。这种操作以反对抗的方式实现&#xff0c;沿着像素梯度的相反方向扰动图像。它迫使最初被认为不具有区…...

curator实现的zookeeper可重入锁

Curator是一个Apache开源的ZooKeeper客户端库&#xff0c;它提供了许多高级特性和工具类&#xff0c;用于简化在分布式环境中使用ZooKeeper的开发。其中之一就是可重入锁。 Curator提供了InterProcessMutex类来实现可重入锁。以下是使用Curator实现ZooKeeper可重入锁的示例&am…...

抽象工厂模式——产品族的创建

1、简介 1.1、简介 抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比&#xff0c;抽象工厂模式中的具体工厂不只是创建一种产品&#xff0c;它负责创建一族产品 1.2、定义 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;&#xff1a;提供…...

【C语言初阶篇】自定义类型结构体我不允许还有人不会!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1 . 什么是结构体1.1 结构的定义1.2 结构的声明 2.结构体初始化2.1 用标签名定义和初始化2.2…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...