1. 定时器
1.1 定时器定义
定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定 好的代码.
定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个Map, 希望里面的某个 key 在 3s 之后过期(自动删除). 类似于这样的场景就需要用到定时器
1.1 标准库中的定时器
- 标准库中提供了一个
Timer 类. Timer 类的核心方法为 schedule . schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后 执行 (单位为毫秒).
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello timer");
}
},3000);
System.out.println("main");
}
1.2 定时器的模拟实现
定时器的构成:
-
一个带优先级的阻塞队列 (PriorityBlockingQueue ) 带优先级的原因: 因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带 优先级的队列就可以高效的把这个 delay 最小的任务找出来. -
队列中的每个元素是一个 Task 对象. -
Task 中带有一个时间属性, 队首元素就是即将执行的任务 -
同时有一个 t 线程一直扫描队首元素, 看队首元素是否需要执行
-
Timer 类提供的核心接口为 schedule, 用于注册一个任务, 并指定这个任务多长时间后执行. -
Task 类用于描述一个任务(作为 Timer 的内部类). 里面包含一个 Runnable 对象和一个 time(毫秒时 间戳) 这个对象需要放到 优先队列 中. 因此需要实现 Comparable 接口. -
Timer 实例中, 通过 PriorityBlockingQueue 来组织若干个 Task 对象. 通过 schedule 来往队列中插入一个个 Task 对象 -
Timer 类中存在一个 t 线程, 一直不停的扫描队首元素, 看看是否能执行这个任务. (所谓 “能执行” 指的是该任务设定的时间已经到达了. )
完整代码(需要注意的点在代码中以注释形式给出,包含测试main代码)
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
private Runnable runnable;
private long time;
public MyTask(Runnable runnable,long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
public void run(){
runnable.run();
}
public long getTime(){
return time;
}
@Override
public int compareTo(MyTask o) {
return (int) (this.time-o.time);
}
}
class MyTimer{
private Object locker=new Object();
private PriorityBlockingQueue<MyTask> queue=new PriorityBlockingQueue<>();
public void schedule(Runnable runnable,long delay){
MyTask task=new MyTask(runnable,delay);
queue.put(task);
synchronized (locker){
locker.notify();
}
}
public MyTimer(){
Thread t=new Thread(()->{
while (true){
try {
MyTask task=queue.take();
long curTime=System.currentTimeMillis();
if (curTime<task.getTime()){
queue.put(task);
synchronized (locker){
locker.wait(task.getTime()-curTime);
}
}else {
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
public class Demo12 {
public static void main(String[] args) {
MyTimer myTimer=new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello timer");
}
},3000);
System.out.println("main");
}
}
|