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

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...