模拟实现定时器(Java版)

前引

定时器是什么?

定时器也是软件开发中的⼀个重要组件. 类似于⼀个 "闹钟". 达到⼀个设定的时间之后, 就执⾏某个指定。

标准库中提供了⼀个 Timer 类. Timer 类的核⼼⽅法为 schedule .
schedule 包含两个参数:
参数1:指定即将要执⾏的任务代码,
参数2:指定多⻓时间之后执⾏ (单位为毫秒)
也就是上述图中的  schedule(TimerTask task, Date time),这里主要用到这个方法,其他的schedule方法这里就不过多介绍了。
Timer类详细介绍,可以点击如下链接

1.思路

 a)先使用一下Timer中schedule方法,了解其功能,这个功能也就是你自己实现时的需求。

public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(3000);
            }
        },3000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(2000);
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(1000);
            }
        },1000);

    }
//打印结果:
//1000
//2000
//3000

通过分析得到大致需求:

1.有一个类似TimerTask的类,说明你要执行的任务和多长时间后执行

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

2.有一个类似Timer的类,类中有schedule方法;执行任务。

class MyTimer{
    private PriorityQueue<MyTimerTask> tasks = new PriorityQueue<>();

    public void schedule(Runnable task, long time){
        MyTimerTask timerTask = new MyTimerTask(task,time);
        addTask(timerTask);
    }
    //执行任务
    public MyTimer(){
        Thread t = new Thread(() -> {
            while (true) {
               //执行。。。。 
        });
        t.start();
    }
    //添加任务
    public void addTask(MyTimerTask task){
        tasks.add(task);
    }
}

3.还要有一数据结构来管理要执行的任务

任务的特点:

1.按照多久后执行的时间从小到大排序

2.会经常插入和删除

这里我选择的是优先级队列 PriorityQueue(从插入算法的时间复杂度分析,查找过程:logN,插入过程:logN)这些是顺序表,链表等达不到的。

private PriorityQueue<MyTimerTask> tasks = new PriorityQueue<>();

2.完整代码

 注意:

1.线程安全问题。

2.重写Comparable中compareTo方法(原因:PriorityQueue 中存放 MyTimer 类不能直接比较)。

3.当优先级队列中为空时,使用 wait() 等待,把调用的资源还给系统,当新加入任务时notify()唤醒。

4.当等待时间最小的任务未达到执行时间时,进行 wait() 等待一定时间,把调用的资源还给系统。

其中:3和4是为了提高对系统资源充分利用,不然就会出现如下代码:

Thread t = new Thread(() -> {
            while (true) {
                synchronized (locker) {
                    if (tasks.isEmpty()) {
                        MyTimerTask task = tasks.peek();
                        if (task.getTime() > System.currentTimeMillis()) {
                            continue;
                        } else {
                            tasks.poll().getTask().run();
                        }
                    }
                }
            }
        });

bug: 当优先级队列为空时或未达到执行时间,上述代码会无休止的进行无意义的判断,浪费大量系统资源。

class MyTimer{
    private PriorityQueue<MyTimerTask> tasks = new PriorityQueue<>();
    private Object locker = new Object();
    //执行任务
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                synchronized (locker) {
                    //问题1:这里为什么使用while而不是if?
                    while (tasks.isEmpty()) {
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    
                    MyTimerTask task = tasks.peek();
                    if (task.getTime() > System.currentTimeMillis()) {
                        try {
                            //问题2:为什么使用wait 而不使用sleep?
                            locker.wait(task.getTime() - System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        tasks.poll().getTask().run();
                    }
                }
            }
        });
        t.start();
    }

    public void schedule(Runnable task, long time){
        MyTimerTask timerTask = new MyTimerTask(task,time);
        synchronized (locker) {
            addTask(timerTask);
            locker.notify();
        }
    }

    //添加任务
    public void addTask(MyTimerTask task){
        tasks.add(task);
    }
}

 解答问题:

问题1:上述代码为什么使用while而不是if?

在你单纯实现定时器时并不会出现什么问题,但在一个项目中有其他的notify不经意间唤醒了怎么办?程序不就报错了吗?使用while,代码执行过程中不就更加安全。

问题2:上述代码为什么使用wait 而不使用sleep?

sleep()方法,不会把资源还给系统,只是死等;当加入一个可以执行的任务时,仍然要在sleep完后才能执行。

wait()方法则会把资源还给系统;当加入一个可以执行的任务时,可以通过notify()唤醒进行任务的执行。

//测试代码
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(3000);
            }
        },3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(2000);
            }
        },2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(1000);
            }
        },1000);
    }
}

 最后:如果你有想法可以与自己项目相结合,进行使用!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

茜子.Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值