这里写目录标题
- 定时器
- 一、定时器是什么
- 二、标准库中的定时器
- 三、实现定时器
定时器
一、定时器是什么
定时器也是软件开发中的⼀个重要组件.类似于⼀个"闹钟".达到⼀个设定的时间之后,就执行某个指定
好的代码.
定时器是⼀种实际开发中⾮常常用的组件.
⽐如⽹络通信中,如果对⽅500ms内没有返回数据,则断开连接尝试重连.
类似于这样的场景就需要用到定时器.
二、标准库中的定时器
-
标准库中提供了⼀个Timer类.Timer类的核⼼⽅法为 schedule .
-
schedule 包含两个参数.第⼀个参数指定即将要执行的任务代码,第⼆个参数指定多⻓时间之后
执行(单位为毫秒).
Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}}, 3000);
三、实现定时器
定时器的构成:
- ⼀个带优先级队列(不要使用PriorityBlockingQueue,容易死锁!)
- 队列中的每个元素是⼀个Task对象.
- Task中带有⼀个时间属性,队⾸元素就是即将要执行的任务
- 同时有⼀个worker线程⼀直扫描队⾸元素,看队⾸元素是否需要执行
详情代码如下:
- Timer类提供的核⼼接⼝为schedule,用于注册⼀个任务,并指定这个任务多⻓时间后执行.
public class MyTimer {public void schedule(Runnable command, long after) {// TODO}
}
-
Task类用于描述⼀个任务(作为Timer的内部类).⾥⾯包含⼀个Runnable对象和⼀个time(毫秒时间戳)
这个对象需要放到优先队列中.因此需要实现 Comparable 接⼝.
class MyTask implements Comparable<MyTask> {public Runnable runnable;// 为了⽅便后续判定, 使用绝对的时间戳.public long time;public MyTask(Runnable runnable, long delay) {this.runnable = runnable;// 取当前时刻的时间戳 + delay, 作为该任务实际执行的时间戳this.time = System.currentTimeMillis() + delay;}@Overridepublic int compareTo(MyTask o) {// 这样的写法意味着每次取出的是时间最⼩的元素.// 到底是谁减谁?? 俺也记不住!!! 随便写⼀个, 执行下, 看看效果~~return (int)(this.time - o.time);}
}
- Timer实例中,通过PriorityQueue来组织若⼲个Task对象.通过schedule来往队列中插⼊⼀个个Task对象.
class MyTimer {// 核⼼结构private PriorityQueue<MyTask> queue = new PriorityQueue<>();// 创建⼀个锁对象private Object locker = new Object();public void schedule(Runnable command, long after) {// 根据参数, 构造 MyTask, 插⼊队列即可.synchronized (locker) {MyTask myTask = new MyTask(runnable, delay);queue.offer(myTask);locker.notify();}}
}
- Timer类中存在⼀个worker线程,⼀直不停的扫描队⾸元素,看看是否能执行这个任务.
所谓"能执行"指的是该任务设定的时间已经到达了.
// 在这⾥构造线程, 负责执行具体任务了.
public MyTimer() {Thread t = new Thread(() -> {while (true) {try {synchronized (locker) {// 阻塞队列, 只有阻塞的⼊队列和阻塞的出队列, 没有阻塞的查看队⾸元素.while (queue.isEmpty()) {locker.wait();}MyTask myTask = queue.peek();long curTime = System.currentTimeMillis();if (curTime >= myTask.time) {// 时间到了, 可以执行任务了queue.poll();myTask.runnable.run();} else {// 时间还没到locker.wait(myTask.time - curTime);}}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();
}