定时器可以帮助我们监管一部分的任务,只需要在规定的时间内自动执行,可以提高我们平常时间效率,下面来一起看看如何实现简单的java定时器

一、定时器

定时器相当于一个任务管理器。有些任务可能现在执行, 有些任务可能过1个小时,甚至很久才会执行。定时器就是对这些任务进行管理监视, 如果一个任务执行时间到了,定时器就会将这个任务执行。 保证所有的任务都会在合适的时间执行。

二、定时器的实现

1、 使用一个MyTask类描述每一个任务

2、 使用优先级队列管理这些任务类。

我们都知道优先级队列底层实现是堆(以小根堆为例), 堆顶的元素是所有的元素的最小值。 我们以任务的定时时间为比较原则构建, 这样就可以保证堆顶元素的任务执行时间是最短的(这样的实现,我们需要在Task类内部定义比较规则-即重写Comparable接口的CompareTo方法),当一个任务执行完毕, 就会从优先级队列取出掉, 然后内部重新组织保证新的堆顶元素是定时时间最短的。

3、使用一个线程循环扫描优先级队列, 相当于一个监控线程,循环判断堆顶任务是否满足执行时间。

三、定时器的组成

1.任务类MyTask

//描述定时器的任务
class MyTask implements Comparable<MyTask> {
    private Runnable runnable;
    //记录定时器的执行时间
    private long time;

    public Runnable getRunnable() {
        return runnable;
    }

    public void setRunnable(Runnable runnable) {
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public MyTask(Runnable runnable, long time) {
        if (time < 0) {
            throw new RuntimeException("延迟时间不能小于等于0");
        }
        this.runnable = runnable;
        //time记录的是任务的具体执行时间
        this.time = time + System.currentTimeMillis();
    }

    @Override
    public int compareTo(MyTask task) {
        if (this.time == task.time) {
            return 0;
        } else if (this.time > task.time) {
            return 1;
        } else {
            return -1;
        }
//            return (int) (this.time-task.getTime());
    }
}

2、扫描线程&定时器对象MyTimer

扫描线程存在一个缺点,就是它会一直扫描监控,(例如队首任务是一小时后,那么它在一小时内会不断的循环扫描),很浪费cpu的资源,这个缺点我们可以通过线程阻塞和唤醒来解决。如果线程判断出当前任务需要1一小时后执行,那么我们让扫描线程wait等待一小时,如果有其他的任务添加进线程,可以重新唤醒线程来进行扫描判断,所以我们定义一个Object类的锁对象来实现阻塞唤醒。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

public class MyTimer {
    //使用优先级队列来保存任务
    private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //定义一个锁对象
    Object locker = new Object();

    //定义一个添加任务的方法
    public void schedule(Runnable runnable, long time) throws InterruptedException {
        MyTask myTask = new MyTask(runnable, time);
        queue.put(myTask);
        synchronized (locker) {
            locker.notifyAll();
        }
    }

    //构造方法
    public MyTimer() {
        //创建扫描线程
        Thread thread = new Thread(() -> {
            //不停的从从队列中取出任务
            while (true) {
                try {
                    //1.取出任务
                    MyTask take = queue.take();
                    //2.判断任务的执行时间是不是到达
                    long currenttime = System.currentTimeMillis();
                    if (currenttime >= take.getTime()) {
                        //如果时间到了执行任务
                        take.getRunnable().run();
                    } else {
                        //如果时间没到,写入队列
                        queue.put(take);
                        //计算一下任务执行时间与当前时间的差值
                        long waittime = take.getTime() - currenttime;
                        synchronized (locker) {
                            locker.wait(waittime);
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        Thread threaddaemon = new Thread(() -> {
            while (true) {
                synchronized (locker) {
                    locker.notifyAll();
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        threaddaemon.setDaemon(true);
        threaddaemon.start();
    }
}

3、测试代码

其中添加了5个任务, 分别是10s、1s、2s、8s、14s后执行。

public class Demo_Timer04 {
    public static void main(String[] args) throws InterruptedException {
        MyTimer myTimer=new MyTimer();
        myTimer.schedule(()->{
            System.out.println("任务1....10s");
        },10000);
        myTimer.schedule(()->{
            System.out.println("任务2....1s");
        },1000);
        myTimer.schedule(()->{
            System.out.println("任务3....2s");
        },2000);
        myTimer.schedule(()->{
            System.out.println("任务4....8s");
        },8000);
        myTimer.schedule(()->{
            System.out.println("任务5....14s");
        },14000);
    }
}
任务2....1s
任务3....2s
任务4....8s
任务1....10s
任务5....14s

执行如下,各个任务按时间执行。