【Java并发编程】手把手教你底层实现定时器

各位看官,大家早安午安晚安呀~~~

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

今天我们来学习【Java并发编程】手把手教你实现定时器

1.Java提供的定时器的使用

 public static void main(String[] args) {
        Timer timer = new Timer();  //我们的定时器类
        timer.schedule(new TimerTask() {   // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
            @Override
            public void run() {
                System.out.println("111");
            }
        },2000);  // 相对于现在的时间延迟1s

        System.out.println("主线程结束");
    }

我们定时器是可以安排多个任务的

public static void main(String[] args) {
        Timer timer = new Timer();  //我们的定时器类
        timer.schedule(new TimerTask() {   // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
            @Override
            public void run() {
                System.out.println("111");
            }
        },1000);  // 相对于现在的时间延迟1s

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

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

        System.out.println("主线程结束");
    }

结果:

总结以下几点注意

1.绝对时间(我们后续比较都是按照绝对时间进行比较,最好在MyTimerTask构造方法里面构造好绝对时间)

2.我们的定时任务要可以进行比较(也可以给优先级队列传一个实现了Comparator接口的类对象(按时间比较MyTimerTask))

3.本身就在多线程这个队列(这个队列不安全,我们本来就要加锁),put和take要加锁别忘了当时(直接再while循环外边的话可以避免频繁地获取锁,更高效一点

4.非空的时候我们才能拿到队首元素(空了进行等待)

5.到点了运行完成记得poll出队列

6.如果没到运行时间(就别一直循环吃cpu资源了,直接原地等到到了时间(计算的相对时间,这个相对时间可不是我们传的相对时间是根据当前的时间计算的),notify唤醒的时候,唤醒哪一个我们都可以再走一遍循环(更新一下时间信息))

2.手撕定时器底层实现

package TimerDemo1;

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Timer;// 在UTil包里面
import java.util.TimerTask;

class MyTimerTask implements Comparable<MyTimerTask> {   // 我们的任务的时间应该能够比较,我们使用优先级队列把我们的任务串联起来
    private Runnable runnable;  //  我们的具体的任务是干哈的
    private long delay;  //  延迟的时间
    private long time;  // 这个任务的发生的绝对时间,一定要是私有的,别人不能更改

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

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time  - o.time);//记得强转,表达式两个括号
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }
}

public class MyTimer {
    Object object = new Object();
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    public void schedule(Runnable runnable , long delay){
        synchronized (object){
            queue.offer(new MyTimerTask(runnable,delay));
            //  刚才队列空了要等待,我们这里放进去元素要给它唤醒
            object.notify();   // 我们打断哪一个wait都可以,要么是要放进来任务了(都是能再走一遍流程)
            //  notify记得加锁呀
        }

    }

    public MyTimer(){
        Thread st  = new Thread(() ->{// 构造方法里面有一个扫描线程
            // 这个锁的位置更高效
            synchronized (object) {    // 本身就在多线程这个队列(这个队列不安全,我们本来就要加锁),put和take要加锁别忘了当时
                while(true) { //直接开始就是不停的扫描
                // 我们直接拿队首元素,一直比较看看到时间了没有
                // 前提的我们有队首元素
                try {
                    while (queue.isEmpty()) {   //如果队列为空我们就等待别的线程放进来任务(while)
                        object.wait();
                    }

                    MyTimerTask task = queue.peek(); // 不能poll,我们只是拿队首元素看一下到没到时间

                    if (System.currentTimeMillis() >= task.getTime()) {
                        task.getRunnable().run();  // 到点了执行
                        queue.poll();  // 记得poll呀
                    } else {  //  也不能就干等着把(这也不是干等着,我们一直循环一直吃着cpu呢) 我们这里应该wait
                        object.wait(task.getTime() - System.currentTimeMillis()); // 等待的时间
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }
            }
        });
        st.start();
    }

}

class Demo1 {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();  //我们的定时器类
        timer.schedule(new Runnable() {   // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
            public void run() {
                System.out.println("111");
            }
        }, 1000);  // 相对于现在的时间延迟1s

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

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("333");
            }
        }, 3000);

        System.out.println("主线程结束");
    }

}

上述就是【Java并发编程】手把手教你实现定时器的全部内容了。定时器的出现,我们可以提前部署很多我们需要的程序,一个很好的工具~~~

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~

您的支持就是我最大的动力​​​!!!