【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();
    }

    @Override
    public 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();
    }

    @Override
    public 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() {
            @Override
            public void run() {
                System.out.println("3000");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2000");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("1000");
            }
        },1000);
    }
}

运行结果 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入解析:如何开发抖音票务小程序

当下&#xff0c;开发抖音票务小程序成为了吸引年轻用户群体的一种创新方式。本文将深入解析如何开发抖音票务小程序&#xff0c;探讨关键步骤和技术要点。 1.确定需求和功能 考虑到抖音的用户特点&#xff0c;可以加入与短视频相关的票务功能&#xff0c;如在线购票、观影记录…

vim+xxd编辑十六进制的一个大坑:自动添加0x0a

问题描述 今天在做一个ctf题&#xff0c;它给了一个elf文件&#xff0c;我要做的事情是修复这个elf文件&#xff0c;最后执行它&#xff0c;这个可执行文件会计算它自身的md5作为这道题的flag。我把所有需要修复的地方都修复了&#xff0c;程序也能成功运行&#xff0c;但是fl…

百度 Comate 终于支持 IntelliJ IDEA 了

大家好&#xff0c;我是伍六七。 对于一直关注 AI 编程的阿七来说&#xff0c;编程助手绝对是必不可少的&#xff0c;除了 GitHub Copilot 之外&#xff0c;国内百度的 Comate 一直是我关注的重点。 但是之前&#xff0c;Comate 还支持 VS code&#xff0c;并不支持 IntelliJ…

Android 相机库CameraView源码解析 (二) : 拍照

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

哈希思想的应用

目录 1.位图 位图的实现 题目变形一 题目变形二 题目变形三 总结&#xff1a; 2.布隆过滤器 概念 布隆过滤器的实现 3.哈希切割的思想 1.位图 哈希表和位图是数据结构中常用的两种技术。哈希表是一种数据结构&#xff0c;通过哈希函数把数据和位置进行映射&#xff0c…

公司人事管理系统

1.问题描述 一个小公司包含四类人员&#xff1a;经理&#xff0c;技术人员&#xff0c;销售人员和销售经理&#xff0c;各类人员的工资计算方法如下&#xff1a;经理&#xff1a;固定月薪&#xff08;8000&#xff09;&#xff1b;技术人员&#xff1a;月薪按技术等级&#xf…

【LeetCode】挑战100天 Day15(热题+面试经典150题)

【LeetCode】挑战100天 Day15&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-172.1 题目2.2 题解 三、面试经典 150 题-173.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

AI视频生成工具——Runway gen2 全功能超详细使用教程(2)

昨天给大家分享了Runway Gen1的使用教程&#xff0c;一篇文章就能让你轻松掌握使用文字和图像从现有视频生成新的视频技能&#xff0c;还没有看过的同学们可以回看过往文章。 Runway视频生成功能有3大核心成品 Gen1&#xff1a;视频转视频工具Gen2&#xff1a;视频生成编辑工…

阅读笔记——《Removing RLHF Protections in GPT-4 via Fine-Tuning》

【参考文献】Zhan Q, Fang R, Bindu R, et al. Removing RLHF Protections in GPT-4 via Fine-Tuning[J]. arXiv preprint arXiv:2311.05553, 2023.【注】本文仅为作者个人学习笔记&#xff0c;如有冒犯&#xff0c;请联系作者删除。 目录 摘要 一、介绍 二、背景 三、方法…

集线器-交换机-路由器

1.集线器(Hub) 集线器就是将网线集中到一起的机器&#xff0c;也就是多台主机和设备的连接器。集线器的主要功能是对接收到的信号进行同步整形放大&#xff0c;以扩大网络的传输距离&#xff0c;是中继器的一种形式&#xff0c;区别在于集线器能够提供多端口服务&#xff0c;也…

Rust UI开发(三):iced如何打开图片(对话框)并在窗口显示图片?

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 这是一个系列博文&#xff0c;本文是第三篇&#xff0c;前两篇的链接&#xff1a; 1、Rust UI开发&#xff08;一&#xff09;&#xff1a;使用iced构建…

2023年09月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 运行下面程序后,角色的x坐标值是?( ) A:100 B:90 C:110 D:120 答案:C 利用变量值作为条件,控制循环的次数。变量从0~10的过程中每次角色的x坐标都增加了10,当变量值为1…

人力资源管理后台 === 左树右表

1.角色管理-编辑角色-进入行内编辑 获取数据之后针对每个数据定义标识-使用$set-代码位置(src/views/role/index.vue) // 针对每一行数据添加一个编辑标记this.list.forEach(item > {// item.isEdit false // 添加一个属性 初始值为false// 数据响应式的问题 数据变化 视图…

牛客 算法 HJ103 Redraiment的走法 golang语言实现

题目 HJ103 Redraiment的走法 实现 package mainimport ("bufio""fmt""os""strconv""strings" )func main() {scanner : bufio.NewScanner(os.Stdin)nums : make([]int, 0)nums_len:0dp:make([]int, 0)for scanner.Scan()…

汇编实验2-2 查找匹配字符串笔记

一、数据段 1.字符串结尾&#xff1a;13,10&#xff0c;$ 2.设置格式控制字符串(这样就不用再写clrf函数了) 3.设置存关键字和句子的地址标签&#xff0c;以关键字为例 二、代码段 1.输入字符串 2.字符串比较 2.1 每次的比较长度&#xff0c;KLEN->CL 2.2 设置目标串起始…

java学习part12多态

99-面向对象(进阶)-面向对象的特征三&#xff1a;多态性_哔哩哔哩_bilibili 1.多态&#xff08;仅限方法&#xff09; 父类引用指向子类对象。 调用重写的方法&#xff0c;就会执行子类重写的方法。 编译看引用表面类型&#xff0c;执行看实际变量类型。 2.父子同名属性是否…

游览器缓存讲解

浏览器缓存是指浏览器在本地存储已经请求过的资源的一种机制&#xff0c;以便在将来的请求中能够更快地获取这些资源&#xff0c;减少对服务器的请求&#xff0c;提高页面加载速度。浏览器缓存主要涉及到两个方面&#xff1a;缓存控制和缓存位置。 缓存控制 Expires 头&#…

力扣每日一题-统计和小于目标的下标对数目-2023.11.24

力扣每日一题&#xff1a;统计和小于目标的下标对数目 开篇 今天这道力扣打卡题写得我好狼狈&#xff0c;一开始思路有点问题&#xff0c;后面就是对自己的代码到处缝缝补补&#xff0c;最后蒙混过关。只能分享一下大佬的代码&#xff0c;然后我帮大家分享代码的思路。 题目链…

84基于matlab的数字图像处理

基于matlab的数字图像处理&#xff0c;数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 84matlab数字图像处理图像增强 (xiaohongshu.com)https://www.xiaohongshu.com/explore/656219d80000000032034dea

python+pytest接口自动化(1)-接口测试基础

一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a; API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或指软件系…