【Java】实现一个自己的定时器

上文讲了怎样使用Java自带的定时器【Java】定时器的简单应用

这篇博客就来讲如何来编写一个自己实现的定时器

1、代码框架

由定时器的使用方法得知,我们在使用定时器的时候会添加一个任务timerTask类,而timer类则是我们行使任务的类,因此可以得出我们需要编写的两个对象:

一个timerTask类,一个timer类

首先写下代码框架

class SelfTimerTask{}class SelfTimer{}public class demo {public static void main(String[] args) {}
}

 2、SelfTimeTask类

这个类型用以存放我们要执行的任务

(1)成员变量

任务类中有两个成员:一个是Runnable类,用来存放要执行任务的内容;一个是参数time,用来存放执行任务的时间

为了防止内存可见性问题或指令重排序问题,给这两个参数都加上volatile关键字

private volatile Runnable runnable;
private volatile long time;

(2)构造方法

接着我们需要给SelfTimerTask类写一个构造方法

注意:上面成员变量time指的是任务执行的绝对时间,而我们传进来的参数delay是任务执行的相对时间(即此刻时间到任务执行绝对时间的差)

任务执行的绝对时间 = 此刻时间 + 相对时间参数

public SelfTimerTask(Runnable runnable,long delay){this.runnable = runnable;this.time = delay + System.currentTimeMillis();
}

(3)get方法

由于两个成员变量访问限制都为private,所以我们需要写两个get方法

public Runnable getRunnable() {return runnable;
}public long getTime() {return time;
}

(4)compareTo方法

因为在任务执行时,要通过比较任务的time参数来进行排序,因此我们需要添加compareTo方法使SelfTimerTask类具有可比性

首先让类继承Comparable类

class SelfTimerTask implements Comparable<SelfTimerTask>

接着,重写compareTo方法

public int compareTo(SelfTimerTask o) {return (int) (this.time - o.time);
}

注意:这里到底谁减谁要根据后面的需求定;可以根据调试来确定谁减谁

(5)SelfTimerTask完整代码

class SelfTimerTask implements Comparable<SelfTimerTask> {private volatile Runnable runnable;private volatile long time;public SelfTimerTask(Runnable runnable,long delay){this.runnable = runnable;this.time = delay + System.currentTimeMillis();}@Overridepublic int compareTo(SelfTimerTask o) {return (int) (this.time - o.time);}public Runnable getRunnable() {return runnable;}public long getTime() {return time;}
}

3、SelfTimer类

编写完SelfTimerTask类,我们来编写SelfTimer类

SelfTimer类是用以按照时间先后顺序执行存储在其中的多个SelfTimerTask类中的任务的,因此我们采用优先级队列的数据结构来编写SelfTimerTask类

定义一个优先级队列

PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();

(1)schedule()方法

根据Timer类的使用可知,SelfTimer有一个schedule()方法来添加任务

public void schedule(Runnable runnable,long time){SelfTimerTask task = new SelfTimerTask(runnable,time);queue.offer(task);
}

由于下面有其他方法也要对queue进行操作,为了线程安全,我们在成员变量里定义一个locker对象

Object locker = new Object();

并给添加任务这段代码加上锁

public void schedule(Runnable runnable,long time){SelfTimerTask task = new SelfTimerTask(runnable,time);synchronized (locker){queue.offer(task);locker.notify();}
}

(2)SelfTimer()方法

因为在用schedule()方法添加任务后,代码自动执行了任务,因此我们需要在构造方法里书写一个线程来执行任务

    public SelfTimer(){Thread thread = new Thread(()->{});thread.start();}

下面来完善线程内代码内容

因为需要不停地扫描任务是否到了执行时间,因此我们采用一个while循环

并且由于下面的代码对queue进行了操作,我们需要加锁来保证线程安全

public SelfTimer(){Thread thread = new Thread(()->{while (true){synchronized (locker){}}});thread.start();}

 · 大根堆还是小根堆? 

由于我们每次执行的是时间已经到达的任务,那么这个任务的time参数一定是最小的

每次需要获取time最小的任务进行操作,当然是选用小根堆

实现小根堆的方法就是重写类中的compareTo()方法,上文已经阐述过,这里不再赘述 

 · 队列为空?

如果队列为空,我们则需要进行阻塞,一直到队列非空为止

另一方面,为了防止线程是发生异常而被唤醒,我们采用while循环进行判断 

while (queue.isEmpty()){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}
}

 · 执行任务

执行任务时,首先判断现在的时间是否已经到达任务执行时间

若已经到了,则执行任务;若没有到,就使任务再阻塞task.getTime()-curTime的时间

之所以选择阻塞,是因为若这时队列中添加进了一个执行时间更靠前的任务,可以唤醒对象重新开始循环

SelfTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (task.getTime() <= curTime){task.getRunnable().run();queue.poll();
}else {try {locker.wait(task.getTime()-curTime);} catch (InterruptedException e) {throw new RuntimeException(e);}
}

 (3)SelfTimer完整代码

class SelfTimer{PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();Object locker = new Object();public void schedule(Runnable runnable,long time){SelfTimerTask task = new SelfTimerTask(runnable,time);synchronized (locker){queue.offer(task);locker.notify();}}public SelfTimer(){Thread thread = new Thread(()->{while (true){synchronized (locker){while (queue.isEmpty()){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}SelfTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (task.getTime() <= curTime){task.getRunnable().run();queue.poll();}else {try {locker.wait(task.getTime()-curTime);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});thread.start();}
}

4、完整代码

import java.util.PriorityQueue;class SelfTimerTask implements Comparable<SelfTimerTask> {private volatile Runnable runnable;private volatile long time;public SelfTimerTask(Runnable runnable,long delay){this.runnable = runnable;this.time = delay + System.currentTimeMillis();}@Overridepublic int compareTo(SelfTimerTask o) {return (int) (this.time - o.time);}public Runnable getRunnable() {return runnable;}public long getTime() {return time;}
}class SelfTimer{PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();Object locker = new Object();public void schedule(Runnable runnable,long time){SelfTimerTask task = new SelfTimerTask(runnable,time);synchronized (locker){queue.offer(task);locker.notify();}}public SelfTimer(){Thread thread = new Thread(()->{while (true){synchronized (locker){while (queue.isEmpty()){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}SelfTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (task.getTime() <= curTime){task.getRunnable().run();queue.poll();}else {try {locker.wait(task.getTime()-curTime);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});thread.start();}
}public class demo {public static void main(String[] args) {SelfTimer timer = new SelfTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000");}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2000");}},2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000");}},1000);}
}

运行结果 

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

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

相关文章

【Apache Doris】Manager极致丝滑地运维管理

【Apache Doris】Manager极致丝滑地运维管理 1.标准VS可视化运维管理2. 环境信息2.1.硬件信息2.2.软件信息 3.前置准备3.1.安装包准备3.2.文档手册准备 4.集群初始化4.1.系统参数预设4.2.Manager部署4.3.新集群部署4.4 监控告警4.4.1 监控4.4.2 告警 5. 集群升级5.1 新包准备5.…

人力资源管理后台 === 登陆+主页鉴权

目录 1. 分析登录流程 2. Vuex中用户模块的实现 3.Vue-cli代理解决跨域 4.axios封装 5.环境区分 6. 登录联调 7.主页权限验证-鉴权 1. 分析登录流程 传统思路都是登录校验通过之后&#xff0c;直接调用接口&#xff0c;获取token之后&#xff0c;跳转到主页。 vue-elemen…

【数据库基础】

目录&#xff1a; 前言什么是数据库主流数据库服务器&#xff0c;数据库&#xff0c;表关系MySQL架构SQL分类存储引擎 前言 剑指offer&#xff1a;一年又1天 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a;…

大量索引场景下 Easysearch 和 Elasticsearch 的吞吐量差异

最近有客户在使用 Elasticsearch 搜索服务时发现集群有掉节点&#xff0c;并且有 master 收集节点信息超时的日志&#xff0c;节点的负载也很高&#xff0c;不只是 data 节点&#xff0c;master 和协调节点的 cpu 使用率都很高&#xff0c;看现象集群似乎遇到了性能瓶颈。 查看…

算法设计与分析(贪心法)

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

中国一年有457万人确诊癌症!医生提示:这4种食物,再爱吃也要管住嘴

癌症是威胁人类生命健康的重大疾病&#xff0c;癌症的发生因素一直以来都是专家学者重点探索的课题。据世卫组织最新公布的数据显示&#xff0c;食物或与癌症发生之间存在着密切的联系&#xff0c;某些食物的摄入过多可能会增加患癌症的风险&#xff0c;所以我们应该警惕&#…

IDEA中JDK21控制台打印的中文乱码

IDEA中&#xff0c;使用的JDK21&#xff0c;控制台打印中文乱码&#xff0c;解决办法是重装了一下JDK。 我之前安装的版本是“jdk-21_windows-x64_bin.exe”&#xff0c;我配置了多个JDK环境&#xff0c;所以使用的是安装文件进行安装的。这次解决乱码问题&#xff0c;我重新安…

代码随想录算法训练营第四十八天|121. 买卖股票的最佳时机、122. 买卖股票的最佳时机 II

LeetCode 121. 买卖股票的最佳时机 题目链接&#xff1a;121. 买卖股票的最佳时机 - 力扣&#xff08;LeetCode&#xff09; 直觉告诉我要贪心算法&#xff0c;章节告诉我得用DP来做&#xff0c;行&#xff0c;都做一下&#xff01; 贪心&#xff1a;只能买一次&#xff0c;所…

Vatee万腾的科技冒险:Vatee独特探索力量的数字化征程

在数字化时代的激流中&#xff0c;Vatee万腾以其独特的科技冒险精神&#xff0c;引领着一场前所未有的数字化征程。这不仅仅是一次冒险&#xff0c;更是对未知的深度探索&#xff0c;将科技的力量推向新的高度。 Vatee万腾在科技领域敢于挑战传统&#xff0c;积极探索未知的可能…

快速解决Navicat连接数据库报错:10061

目录 问题原因&#xff1a; 错误提示&#xff1a; 解决方案&#xff1a; 问题1&#xff1a;如何进入指定目录&#xff1f; 问题2&#xff1a;若出现&#xff1a;“服务名无效” 将MySQL注册到win服务中 问题原因&#xff1a; mysql服务没有开启&#xff08;可能会在更新windows…

C#,《小白学程序》第三课:类class,类的数组及类数组的排序

类class把数值与功能巧妙的进行了结合&#xff0c;是编程技术的主要进步。 下面的程序你可以确立 分数 与 姓名 之间关系&#xff0c;并排序。 1 文本格式 /// <summary> /// 同学信息类 /// </summary> public class Classmate { /// <summary> /…

前端实现埋点

前端实现埋点 如何去了解用户呢&#xff1f;最直接有效的方式就是了解用户的行为&#xff0c;了解用户在网站中做了什么&#xff0c;呆了多久。而如何去实现这一操作&#xff0c;这就涉及到我们前端的埋点了。 埋点方式 什么是埋点&#xff1f; 所谓埋点是数据采集领域&…

【python】--文件/文件夹读写及操作

目录 一、文件读写1、文件读写代码示例 二、文件/文件夹操作1、代码示例 一、文件读写 读写文件就是请求操作系统打开一个文件对象&#xff08;通常称为文件描述符&#xff09;&#xff0c;然后通过操作系统提供的接口从这个文件对象中读取数据&#xff08;读文件&#xff09;…

机器人规划算法——movebase导航框架源码分析

这里对MoveBase类的类成员进行了声明&#xff0c;以下为比较重要的几个类成员函数。 构造函数 MoveBase::MoveBase | 初始化Action 控制主体 MoveBase::executeCb收到目标&#xff0c;触发全局规划线程&#xff0c;循环执行局部规划 全局规划线程 void MoveBase::planThread |…

学习笔记:如何分析财务报表

其实财务报表分析最核心的东西&#xff0c;是通过财务报表这个结果&#xff0c;由果推因&#xff0c;找出造成这个结果的原因。 会计是商业的语言 首先第一个问题是——会计是商业的语言&#xff0c;这是会计的根本。 什么叫“语言”&#xff0c;就是可以通过它进行交流。比如…

用队列和栈分别实现栈和队列

用队列实现栈 题目解读 本题的要求是要用两个队列来实现一个先进后出的栈&#xff0c;并且要有以下功能&#xff1a; 1.将元素压入栈中 2.移除栈顶元素并且返回他 3.返回栈顶元素 4.判断栈是否为空 题目构思和代码实现 我们首先要做的就是将实现队列的代码导入该题&#xff…

SSM 框架整合

1 整合配置 1.1 流程 1.2 Spring 整合 MyBatis 1.3 Spring 整合 SpringMVC 1.4 配置代码 JdbcConfig.java public class JdbcConfig {Value("${jdbc.driver}")private String driver;Value("${jdbc.url}")private String url;Value("${jdbc.usern…

四、IDEA创建项目时,Maven Archetype模板工程说明

什么是Maven Archetype Archetype是一个Maven项目的模板工具包&#xff0c;它定义了一类项目的基本架构。Archetype为开发人员提供了创建Maven项目的模板&#xff0c;同时它也可以根据已有的Maven项目生成参数化的模板。 官方文档&#xff1a;https://maven.apache.org/archet…

cuda magma 构建 使用cmake构建的步骤记录

这不是群论代数软件&#xff0c;而是cuda 矩阵计算软件 1. 生成其他精度的源代码 1.1 复制编辑 make.inc cp make.inc-examples/make.inc.openblas ./make.inc 并修改其中的定义&#xff1a; OPENBLASDIR ? /opt/OpenBLAS 这需要实现安装openblas到此处。文件夹解构&…

【通讯协议】REST API vs GraphQL

在API设计方面&#xff0c;REST和GraphQL各有缺点。下图显示了 REST 和 GraphQL 之间的快速比较。 REST 使用标准 HTTP 方法&#xff08;如 GET、POST、PUT、DELETE&#xff09;进行 CRUD 操作。当您需要在单独的服务/应用程序之间提供简单、统一的接口时&#xff0c;效果很好…