目录
一、定时器是什么
定时器是软件开发的一个重要组件,类似于一个“闹钟”,达到一个设定的时间之后,就执行某个指定好的代码。
二、定时器的使用
1、标准库中提供了一个Timer类,Timer类的核心方法为schedule;
2、schedule包含两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行(单位为毫秒)
代码案例:
public class ThreadDemo15 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello4");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello3");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello2");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello1");
}
},1000);
System.out.println("hello");
}
}
测试结果:
三、自定义实现一个定时器
定时器的构成:
1、一个带优先级的阻塞队列
为什么要带优先级呢?
因为阻塞队列中的任务都有各自的执行时刻,最先执行的任务一定是delay最小的,使用带优先级队列这种数据结构就可以高效把这个delay最小的任务找出来。
2、队列中的每个元素是一个Task对象。
3、Task中带有一个时间属性,队首元素就是即将要执行的任务。
4、同时会有一个线程一直扫描队首元素,看队首元素是否需要执行。
1、首先构建一个MyTask类,表示一个任务
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;
}
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
2、实现一个定时器(内置一个扫描线程)
扫描线程的弊端:一直循环判断,占用系统的资源。(例如我们一个小时后去上课,但是在这一小时内我们反复进行看时间的工作,这样会消耗资源,这里也就是代码中频繁的出队列操作)。
解决办法:可以通过线程阻塞和唤醒来解决这种弊端。(例如如果有一个任务一个小时后进行,我们会让扫描线程wait一个小时,但是在此期间如果有新的任务需要执行,我们就可以采用notify来进行唤醒操作)
注意:这里的wait不能替换成sleep,因为sleep是指定一个线程休眠多长时间(例如如果有一个任务一个小时后进行,我们会让sleep休眠一个小时,但是在此如果有新的任务执行,我们以下休眠过头导致错过此任务)。
class MyTimer {
//带优先级的阻塞队列
private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
Object locker = new Object();
public void schedule(Runnable runnable, long delay) {
//构造MyTask
MyTask myTask = new MyTask(runnable, delay);
queue.offer(myTask);
synchronized (locker) {
locker.notify();
}
}
public MyTimer() {
Thread t = new Thread(() -> {
while (true) {
try {
synchronized (locker) {
MyTask myTask = queue.take();
long curTime = System.currentTimeMillis();
if (myTask.time <= curTime) {
//时间到了可以执行任务
myTask.runnable.run();
} else {
//时间还没到
queue.offer(myTask);
locker.wait(myTask.time - curTime);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
测试代码:
public class ThreadDemo16 {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello4");
}
}, 4000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello3");
}
}, 3000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello2");
}
}, 2000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello1");
}
}, 1000);
System.out.println("hello");
}
}
测试结果: