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

news/2024/7/24 11:18:12 标签: java, 开发语言

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

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

1、代码框架

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

一个timerTask类,一个timer类

首先写下代码框架

java">class SelfTimerTask{
    
}

class SelfTimer{
    
}

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

 2、SelfTimeTask类

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

(1)成员变量

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

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

java">private volatile Runnable runnable;
private volatile long time;

(2)构造方法

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

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

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

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

(3)get方法

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

java">public Runnable getRunnable() {
    return runnable;
}

public long getTime() {
    return time;
}

(4)compareTo方法

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

首先让类继承Comparable类

java">class SelfTimerTask implements Comparable<SelfTimerTask>

接着,重写compareTo方法

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

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

(5)SelfTimerTask完整代码

java">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类

定义一个优先级队列

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

(1)schedule()方法

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

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

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

java">Object locker = new Object();

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

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

(2)SelfTimer()方法

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

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

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

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

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

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

 · 大根堆还是小根堆? 

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

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

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

 · 队列为空?

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

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

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

 · 执行任务

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

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

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

java">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完整代码

java">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、完整代码

java">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.niftyadmin.cn/n/5216226.html

相关文章

MyBatis-Plus简介和入门操作

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

皮尔逊相关性分析的matlab实现,简介和实例

皮尔逊相关性分析&#xff08;Pearson correlation analysis&#xff09;是一种常用的统计方法&#xff0c;用于衡量两个变量之间的线性关系强度和方向。它通过计算两个变量之间的协方差和标准差来衡量它们之间的相关性。皮尔逊相关系数的取值范围为 -1 到 1&#xff0c;其中 -…

【Android】Android Framework系列--Launcher3各启动场景源码分析

Android Framework系列–Launcher3各启动场景源码分析 Launcher3启动场景 Launcher3是Android系统提供的默认桌面应用(Launcher)&#xff0c;它的源码路径在“packages/apps/Launcher3/”。 Launcher3的启动场景主要包括&#xff1a; 开机后启动&#xff1a;开机时&#xff…

Java中wait()方法在synchronized方法中调用的奥秘

作为一名Java程序员&#xff0c;我们深知synchronized关键字和wait()方法在多线程编程中的重要性。 在本文中&#xff0c;我们将探讨为什么wait()方法需要在synchronized方法中调用&#xff0c;以及它们是如何协同工作的。 首先&#xff0c;让我们了解一下synchronized关键字和…

目录树自动生成器 golang+fyne

go tree 代码实现请看 gitee 仓库链接 有很多生成目录树的工具&#xff0c;比如windows自带的tree命令&#xff0c;nodejs的treer&#xff0c;tree-cli等等。这些工具都很成熟、很好用&#xff0c;有较完善的功能。 但是&#xff0c;这些工具全部是命令式的&#xff0c;如果…

Royal TSX v6.0.1

Royal TSX是一款基于插件的软件&#xff0c;适用于Windows系统&#xff0c;可以用于远程连接和管理服务器。它支持多种连接类型&#xff0c;如RDP、VNC、基于SSH连接的终端&#xff0c;SFTP/FTP/SCP或基于Web的连接管理。 在安装Royal TSX后&#xff0c;需要进行一些基础配置&…

从代码执行,看单片机内存的分配

1、单片机执行指令过程详解 单片机执行程序的过程&#xff0c;实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行&#xff0c;即取指令--分析指令--执行指令。 取指令的任务是&#xff1a;根据程序计数器PC中的值从程序存储器读…

2023年最新Visual Studio下载安装以及C语言环境搭建教程(含C语言入门教程)

文章目录 写在前面C语言简介Visual Studio简介Visual Studio安装教程 C语言入门Visual Studio使用教程 写在后面 写在前面 2023年最新Visual Studio下载安装以及C语言环境搭建教程来啦&#xff01;一起来看看吧~ C语言简介 C语言是一种高级编程语言&#xff0c;由美国贝尔实…