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

news2024/11/27 0:48:49

上文讲了怎样使用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);
    }
}

运行结果 

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

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

相关文章

混社会,要知道的“人性铁律”

混社会&#xff0c;要知道的“人性铁律”&#xff1a; 1.女人的私密生活&#xff0c;生理需求往往都是心口不一。 【闲聊】&#xff1a;一个人越是想要什么&#xff0c;往往嘴里越是说不在意&#xff0c;无论是男女。 2.普通人的思维&#xff0c;往往把宏大的社会变迁所造成…

告别软件代码,硬件攻城狮也能DIY的 PD DRP+OTG 芯片来了

随着 USB-C 接口的普及&#xff0c;越来越多的设备开始采用这种接口。由于 USB-C接口的高效性和便携性&#xff0c;使各种设备之间的连接和数据传输变得非常方便快捷&#xff0c;它们不仅提供了强大的功能&#xff0c;还为我们的日常生活和工作带来了极大的便利&#xff0c;USB…

人力资源管理后台 === 角色管理

目录 1.组织架构-编辑部门-弹出层获取数据 2.组织架构-编辑部门-编辑表单校验 3.组织架构-编辑部门-确认取消 4.组织架构-删除部门 5.角色管理-搭建页面结构 6.角色管理-获取数据 7.角色管理-表格自定义结构 8.角色管理-分页功能 9.角色管理-新增功能弹层 10.角色管理…

C语言实现万年历

C语言实现万年历 一、项目介绍 需求和功能是用纯C语言实现一个可以属于年份&#xff0c;属于一个年份就可以显示该年各个月份的日历&#xff0c;如同日历一般&#xff0c;每个月当中每天对应的星期均可查看&#xff0c;即万年历&#xff0c;要求格式整齐&#xff0c;星期对照直…

5、Hydra与Crunch基本使用

文章目录 一、关于Hydra与Crunch二、在操作机上使用Crunch生成用户名和密码字典三、在操作机上使用Hydra对靶机FTP登录密码进行字典攻击 一、关于Hydra与Crunch Hydra&#xff08;九头蛇&#xff09;是一个相当强大的暴力密码破解工具。该工具支持几乎所有协议的在线密码破解&…

基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码

基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于驾驶训练优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap

在 Spring Boot 项目中&#xff0c;我们经常使用 YAML 文件来配置应用程序的属性。在这篇博客中&#xff0c;我将模拟如何在 Java 的 Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap。 1. 介绍 YAML&#xff08;YAML Aint Markup Language&#xff09;是一种人类…

《数据结构与算法之美》读书笔记2

链表操作的技巧 1.理解指针 将摸个变量赋值给指针&#xff0c;实际上就是将这个变量的地址赋给指针&#xff0c;或者&#xff0c;指针中存储了这个变量的地址&#xff0c;指向了这个变量&#xff0c;所以可以通过指针找到这个变量。 2.内存泄漏或指针丢失 删除链表节点时&a…

对称加密与非对称加密的区别是什么?

对称加密与非对称加密的区别是什么&#xff1f; 对称加密概念&#xff1a;好处和坏处&#xff1a;基本原理 非对称加密概念&#xff1a;工作原理&#xff1a; 两者区别安全性处理速度密钥管理通信双方数量 对称加密 概念&#xff1a; 同一个密钥可以同时用来对信息进行加密和…

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

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

老师应具备什么样的心理素质

老师&#xff0c;一个充满智慧与挑战的职业&#xff0c;就像园丁&#xff0c;用无私的爱和耐心&#xff0c;滋养着每一颗渴望知识的幼苗。那么&#xff0c;作为教育从业者&#xff0c;要具备哪些心理素质呢&#xff1f; 强大的情绪管理能力 老师的工作绝非一帆风顺。在教育学生…

JAVA - 阻塞队列

一、什么是堵塞队列 堵塞队列&#xff08;Blocking Queue&#xff09;是一种特殊类型的队列&#xff0c;它具有一些特定的行为和限制。在堵塞队列中&#xff0c;当队列为空时&#xff0c;尝试从队列中取出元素的操作将会被阻塞&#xff0c;直到队列中有可用元素&#xff1b;当…

【社会网络分析第6期】Ucient实操

一、导入数据处理二、核心——边缘分析三、聚类分析四、网络密度 一、导入数据处理 将数据导入Ucinet首先需要对数据进行处理。 承接上一期的数据格式&#xff1a;【社会网络分析第5期】gephi使用指南 原先得到的数据格式如下&#xff1a; 接下来打开ucinet&#xff1a; 之后…

免费 2 个月!ChatGPT-4 和 Claude 2 都能用

你好&#xff0c;我是 EarlGrey&#xff0c;一名双语学习者&#xff0c;会一点编程&#xff0c;目前已翻译出版《Python 无师自通》、《Python 并行编程手册》等书籍。 点击上方蓝字关注我&#xff0c;获取最新编程及AI干货、高赞工具和项目分享。 在后台回复“books”&#xf…

10、鸿蒙组件的实现语法

1、查看在线开发文档 在开发工具的右边有API帮助&#xff0c;打开后可以查阅需要的内容 2、创建组件的语法 组件名(options) options组件初始化参数。比如创建Text文本组件&#xff0c;options是显示的文本内容。 Text(我是一个文本) 3、配置组件属性 组件的属性一般用于…

【数据库基础】

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

npm中的npx命令

1.概念 npx是一个执行npm软件包的二进制文件&#xff0c;通俗的讲&#xff0c;他可以执行npm的一些指令。 2.示例 用babel将ES6语法转为ES5语法 npx babel src/js -d dist/js会执行babel的相关功能&#xff0c;如果没有安装&#xff0c;也会自动安装。 当在执行npx <co…

单片机学习3——数码管

数码管&#xff0c;根据内部结构&#xff0c;可分为共阴极数码管和共阳极数码管。七段发光管加上一个小数点&#xff0c;共计8段。因此&#xff0c;我们对它编程的时候&#xff0c;刚好是用一个字节。 数码管的显示方式&#xff1a; 1&#xff09;静态显示&#xff1b; 2&…

【刷题笔记】数组-双指针||覆盖||重复元素

【刷题笔记】数组-双指针||覆盖||重复元素 目录 移除元素删除有序数组中的重复项删除有序数组中的重复项 II分析 移除元素 https://leetcode.cn/problems/remove-element/ 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并…

90. 打家劫舍II (房子围成一圈)

题目 题解 class Solution:def rob(self, nums: List[int]) -> int:def dp(nums: List[int]) -> int:N len(nums)# 定义状态&#xff1a;dp[i]表示从第i个房屋开始偷窃&#xff0c;能够偷到的最高金额dp [0 for i in range(N)]for i in range(N-1, -1, -1):if i N-1:…