定时器--JAVA

发布时间:2024年01月15日

定时器是软件开发中的一个重要组件,类似于一个"闹钟"当达到一个设定的时间之后,就执行某个指定好的代码(任务)。

Timer

JAVA标准库中已经为我们实现了一个定时器,我们直接new就行了。

Timer timer = new Timer();

Timer类中最重要的一个方法就是schedule(),这个方法用于设置定时器待执行的任务和执行任务的时间。

public static void main(String[] args) {
    Timer timer = new Timer();
    //在3秒后打印3000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("3000");
        }
    }, 3000);
    //在2秒后打印2000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("2000");
        }
    }, 2000);
    //在1秒后打印1000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("1000");
        }
    }, 1000);
    
    System.out.println("OK");
}

可以发现在代码执行完毕后程序并没有结束,这是因为虽然主线程结束了但是Timer类中的线程在阻止程序结束,它们还在等待新的任务进来被执行。

为了可以更好的理解定时器的原理,下面进行简单的模拟实现。

模拟实现

首先我们需要先创建一个MyTimerTask类,该类主要用来保存待执行的任务,和执行该任务的时间点。

class MyTimerTask {
    public Runnable runnable;
    //存储绝对时间,后期直接和当前时间比较大小就行
    public long time;

    public MyTimerTask(Runnable runnable, long time){
        this.runnable = runnable;
        this.time = time + System.currentTimeMillis();
    }
}

接着写我们的定时器类MyTimer。首先我们应该先选择一种数据结构来存储所有的MyTimerTask,这里我推荐使用优先级队列,也就是堆。因为如果使用数组或链表来存储就需要你不断地遍历该数组/链表,而使用优先级队列就可以只检查队首元素是否到执行时间。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
}

因为我们使用了优先级队列所以我们需要让MyTimerTask类可以进行比较。

class MyTimerTask implements Comparable<MyTimerTask>{
    ……
    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (this.time-o.time);
    }
}

接着我们需要实现一个schedule方法可以接收定时任务和时间,因为是多线程代码所以我们还应该加一个属性用来充当锁对象。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
        }
    }
}

此时我们还需要一个自动检查队列的线程,而且该线程并不需要程序员来启动和创建,所以我们可以将它写在构造方法中。

public MyTimer() {
    Thread t = new Thread(()->{
        while(true) {
            synchronized (lock) {
                if (priorityQueue.isEmpty()) {
                    //堆为空           
                }
                MyTimerTask myTimerTask = priorityQueue.peek();
                if (myTimerTask.time <= System.currentTimeMillis()) {
                    //执行当前任务
                    myTimerTask.runnable.run();
                    //将当前任务移除
                    priorityQueue.poll();
                }else {
                    //时间没到
                }
            };
        }
    });
    t.start();
}

此处我们可以用阻塞队列的思想,当堆为空或者执行时间没到就使用wait()进行等待。有人添加任务时就使用notify()进行唤醒线程。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
            lock.notify();
        }
    }

    public MyTimer() {
        Thread t = new Thread(()->{
            while(true) {
                synchronized (lock) {
                    if (priorityQueue.isEmpty()) {
                        //如果堆为空就阻塞等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTimerTask myTimerTask = priorityQueue.peek();
                    if (myTimerTask.time <= System.currentTimeMillis()) {
                        //执行当前任务
                        myTimerTask.runnable.run();
                        priorityQueue.poll();
                    }else {
                        //时间没到就阻塞等待
                        try {
                            lock.wait(myTimerTask.time-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }
        });
        t.start();
    }
}

简单测试

public static void main(String[] args) {
    MyTimer timer = new MyTimer();
    //在3秒后打印3000
    timer.schedule(()->{
            System.out.println("3000");
    }, 3000);
    //在2秒后打印2000
    timer.schedule(()->{
            System.out.println("2000");
    }, 2000);
    //在1秒后打印1000
    timer.schedule(()->{
            System.out.println("1000");
    }, 1000);

    System.out.println("OK");
}

完整代码

import java.util.PriorityQueue;

class MyTimerTask implements Comparable<MyTimerTask>{
    public Runnable runnable;
    //存储绝对时间,后期直接和当前时间比较大小就行
    public long time;

    public MyTimerTask(Runnable runnable, long time){
        this.runnable = runnable;
        this.time = time + System.currentTimeMillis();
    }

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

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
            lock.notify();
        }
    }

    public MyTimer() {
        Thread t = new Thread(()->{
            while(true) {
                synchronized (lock) {
                    if (priorityQueue.isEmpty()) {
                        //如果堆为空就阻塞等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTimerTask myTimerTask = priorityQueue.peek();
                    if (myTimerTask.time <= System.currentTimeMillis()) {
                        //执行当前任务
                        myTimerTask.runnable.run();
                        priorityQueue.poll();
                    }else {
                        //时间没到就阻塞等待
                        try {
                            lock.wait(myTimerTask.time-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }
        });
        t.start();
    }
}

文章来源:https://blog.csdn.net/2302_76339343/article/details/135511145
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。