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关键字。防止出现意料之外的错误。

相关推荐
dr_yingli2 分钟前
fMRI(3-1)报告(个体化报告)生成器说明
开发语言·matlab
hrhcode2 分钟前
【java工程师快速上手go】一.Go语言基础
java·开发语言·golang
l1t4 分钟前
用wsl自带的python 3.10下载适用于3.12的pandas版本结合uv安装python 3.12模拟离线安装场景
python·pandas·uv
2601_9507039416 分钟前
Spring IoC入门实战:XML与注解双解
java
带刺的坐椅17 分钟前
Snack JSONPath 项目架构分析
java·json·java8·jsonpath
飞Link25 分钟前
【AI大模型实战】万字长文肝透大语言模型(LLM):从底层原理解析到企业级Python项目落地
开发语言·人工智能·python·语言模型·自然语言处理
妙蛙种子31126 分钟前
【Java设计模式 | 创建者模式】 原型模式
java·开发语言·后端·设计模式·原型模式
翻斗包菜29 分钟前
第 03 章 Python 操作 MySQL 数据库实战全解
数据库·python·mysql
LlNingyu31 分钟前
Go 实现无锁环形队列:面向多生产者多消费者的高性能 MPMC 设计
开发语言·golang·队列·mpmc·数据通道
Lyyaoo.33 分钟前
【JAVA基础面经】线程的状态
java·开发语言