文章目录
一、认识定时器
二、自主实现定时器
1.明确定时器的内核原理
2.定时器框架搭建
3.优先级队列中的比较问题
4.“忙等”问题
5. 代码中随机调度的问题
三、整体代码罗列
本篇文章的相关代码本人已经上传至本人的 gitee Timer。
一、认识定时器
什么是定时器 定时器是我们在日常的软件开发中很重要的一个组件。类似于闹钟,当到达设定时间后就去执行某个指定的代码。 举一个非常常见的例子,在网络编程中,经常会出现 “卡了”、“连不上” 这样的情况,此时定时器就会设定在多长时间之后进行重连操作,或者停止等待,这就是定时器的一个简单使用。
2.标准库中的定时器运用
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo {
public static void main(String[] args) {
System.out.println("程序启动");
//Timer 是标准库的定时器
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务1");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务2");
}
},2000);
}
}
二、自主实现定时器
1.明确定时器的内核原理
根据上面标准库中对定时器的使用,我们可以知道,要实现一个定时器需要实现以下功能:
让被注册的任务在指定的时间被执行。
定时器可以注册 N 个任务,这 N 个任务会按照最初的设定的时间,按顺序执行。
针对第一点:要在指定时间内执行线程。 要满足这个条件,需要单独在定时器内部设定一个扫描线程,让这个扫描线程周期性对任务进行扫描,判断是否到达时间,是否执行。
针对第二点:定时器可以注册 N 个任务。 很显然,这里需要一个数据结构来保存。 我们知道,这里的每个任务都带着一个重要元素 时间,并且要求 时间越靠前,越先执行。 到此,已经非常明显,优先级队列无疑是很好的一个选择。
在已知使用 优先级队列 之后,线程扫描也变得更加容易实现,这种情况下,只需要扫描队首元素即可,无序遍历整个队列。
2.定时器框架搭建
如图所示: 要实现一个阻塞式优先级队列,需要指定元素类型,这里的 任务 可以使用 Runnable 来表示,除此之外,我们还需要描述任务什么时候执行。
根据上面的描述,我们已经有了大致的方向,下面,我们先实现一个类,将 任务 和 时间 进行包装,成为阻塞式优先级队列的元素类型,代码如下:
实现类型方法
class MyTask{
//要执行的任务内容
private Runnable runnable;
//任务在什么时间执行(使用毫秒级时间戳)
private long time;
public MyTask(Runnable runnable, long time) {
this.runnable = runnable;
this.time = time;
}
//获取任务当前的时间
public long getTime(){