Java模拟实现定时器

定时器的基本使用

Java中的定时器是Timer,通过调用Timer对象的schedule方法传入任务和延迟时间。如下:

java 复制代码
    public static void main1(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask(){
            @Override
            public void run(){
                System.out.println("1000");
            }
        },1000);
    }

以上代码表示:在1秒后打印 1000.

运行以上代码后发现,程序一直没有退出,说明在timer对象中创建了一个线程,这个线程正在等待我们派发任务,所以一直没有退出。

定时器的模拟实现

要模拟实现定时器就需要确认3个东西:1. 创建对象来存储用户传入的任务和延迟时间。2. 使用某种数据结构来存储用户传入的任务。3. 需要一个线程不断的扫描这个数据结构中是否有任务未被执行。

那么使用哪种数据结构呢? 优先级队列是是被考虑的,因为这些定时任务的优先级就是按照延迟时间进行排序的,延迟时间越短,那么优先级就越高。

由于优先级队列中的元素是可比较的,故必须对存放延迟时间和任务的类实现Comparable接口,重写Comparator方法,通过时间戳进行比较。

所以我们创建一个MyTimerTask对象来存储用户的任务和延迟时间。如下:

java 复制代码
class MyTimerTask implements Comparable<MyTimerTask>{
    // 任务执行的绝对时间
    private Long time;
    // 任务
    private Runnable runnable;
    MyTimerTask(Runnable runnable ,Long time){
        this.runnable = runnable;
        this.time = System.currentTimeMillis()+time;
    }
    public Long getTime(){return this.time;}
    public Runnable getRunnable(){return this.runnable;}

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time - o.time);
    }
}

注意:这里没有记录用户传入的延迟时间,而是直接记录这个任务要完成的绝对的时间戳。

接下来就需要创建MyTimer类了。这个类需要包含:

  1. 优先级队列
  2. 线程(用于扫描优先级队列中是否有任务待执行)
  3. schedule方法(第一个参数,Runnable对象
java 复制代码
class MyTimer{
    private static volatile Object locker = new Object();
    // 优先级队列
    PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    public void schedule(Runnable runnable,Long time){
        synchronized (locker){
            // 创建对象
            queue.offer(new MyTimerTask(runnable,time));
            // 创建好对象之后,就唤醒。
            locker.notify();
        }
    }
    // 搞一个扫描线程
    public MyTimer(){

        Thread t = new Thread(()->{
            // 需要不停的扫描是否有任务。
            while(true){
                synchronized (locker){
                    while(queue.isEmpty()){
                        // 为空,则需要等待
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTimerTask task = queue.peek();
                    Long time = task.getTime();
                    Runnable runnable = task.getRunnable();
                    long currentTime = System.currentTimeMillis();
                    // 满足,
                    if(currentTime >= time){
                        runnable.run();
                        queue.poll();
                    }else {
                    try {
                        locker.wait(time-currentTime);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    }
                }
            }
        });
        t.start();
    }
}

注意:这个扫描线程需要不停的去扫描优先级队列中受否有任务待执行,所以需要使用while循环。

注意:在判断当前的时间是否是大于定时器执行的时间时,如果不满足,那么不需要进行下一次的while循环,可以等待time-currentTime的时间。这样就可以防止忙等的情况发生。

直到优先级队列中的第一个任务完成之后,这个线程才会继续执行优先级队列中的下一个任务。这是通过MyTimerTask是通过时间戳比较器来实现的。

注意:为了防止指令重排序的情况发生和内存不可见性,建议对加锁中的代码块中要被操作的变量加上volatile关键字。防止出现意料之外的错误。

相关推荐
froginwe1110 分钟前
DOM 加载函数
开发语言
lzhdim12 分钟前
SQL 入门 12:SQL 视图:创建、修改与可更新视图
java·大数据·服务器·数据库·sql
Hello eveybody26 分钟前
介绍一下背包DP(Python)
开发语言·python·动态规划·dp·背包dp
2301_7950997439 分钟前
让 CSS Grid 自适应容器尺寸的动态布局方案
jvm·数据库·python
AI进化营-智能译站42 分钟前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
呆萌的代Ma44 分钟前
python读取并加载.env的配置文件
python
Muyuan19981 小时前
27.RAG 系统中的上下文充分性判断:从 Chunk 数量、FAISS 距离到 LLM Relevance Gate
python·django·pdf·fastapi·faiss
iCxhust1 小时前
微机原理实践教程(C语言篇)---A002流水灯
c语言·开发语言·单片机·嵌入式硬件·51单片机·课程设计·微机原理
FQNmxDG4S1 小时前
Maven依赖管理:版本冲突解决与生命周期控制
java·数据库·maven
莎士比亚的文学花园1 小时前
Linux驱动开发(3)——设备树
开发语言·javascript·ecmascript