JAVA中的定时器
在java标准库中,也有定时器的实现。
Timer,Timer的schedule有两个参数,第一个是要执行的代码,第二个是等待时间。
public class Thread24 {
public static void main(String[]args){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {//时间到后,要执行的代码。
System.out.println("Test");
}
},3000);//等待时间
}
}
程序会在3秒后打印Test,一般代码是不会主动结束的,需要用cancle方法主动结束。
实现一个Timer
Timer里面的内容:
1)需要有一个线程,负责记录时间,等任务到达合适时间,这个线程就负责执行。
2)需要有一个队列/数组,用来保存schedule进来的任务。
但是如果队列很长,遍历的过程开销就会很大。
所以可以使用优先级数列,优先级数列中只需要关注队首元素是否到时间,如果队首没到时间,后续元素也一定没到时间,就不必遍历队列了。
就可以使用标准库提供的PriorityQueue(线程不安全,但可以通过手动加锁来控制),还有PriorityBlockingQueue(线程安全,但是不好控制),所以使用第一个。
时间戳:以1970年1月1日为基准,计算当前时刻和基准时刻的,秒数/毫秒数/微秒数.......之差。
我们引入时间戳来确定什么时间执行任务。
而且在多线程情况下,我们还要通过加锁来保证线程安全,因为设计到队列的取元素和删除元素。
package Thread;
import java.util.Objects;
import java.util.PriorityQueue;
class MyTimerTask implements Comparable<MyTimerTask>{
private long time;//任务执行时间
private Runnable runnable;
public MyTimerTask(long delay, Runnable runnable) {
this.runnable = runnable;
this.time = System.currentTimeMillis()+delay;//由于delay是相对时间。
}
public void run(){
runnable.run();
}
//任务执行的代码
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.time-o.time);
}
public long getTime(){
return time;
}
}
class MyTimer{
private Thread t = null;//扫描线程
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//任务队列
Object lock = new Object();
public void schedule(Runnable runnable,long delay){
synchronized (lock){
MyTimerTask task = new MyTimerTask(delay,runnable);
queue.offer(task);
//添加任务后,就可以唤醒扫描线程的wait了
lock.notify();
}
}
//构造方法,创建扫描线程,让扫描线程来完成判定和执行
public MyTimer(){
t = new Thread(()->{
//扫描线程需要反复扫描队列首元素,判断首元素时间是否到了
//没到,啥也不干
//到了,执行任务并把这个任务删除
while(true) {
try {
synchronized (lock) {
while(queue.isEmpty()) {
lock.wait();//如果队列为空,继续获取元素是无意义的,所以要加上wait。
}
MyTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (curTime >= task.getTime()) {
//时间到了,可以执行了
queue.poll();
task.run();
} else {
lock.wait(task.getTime()-curTime); //时间没到,暂时不执行
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
t.start();
}
}
public class Thead25 {
public static void main(String[]args){
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3000");
}
},3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 2000");
}
},2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 1000");
}
},1000);
System.out.println("hello main");
}
}
有一说一,这个代码确实麻烦,而且涉及到wait和notify,导致代码并不是按照我们认知中的顺序执行,需要我们有多线程思想,对于我这个初学者确实麻烦...........
代码的运行结果应该是这样的:
hello main
hello 1000
hello 2000
hello 3000
而且由于线程是在MyTimer里创建的,所以它并不会主动终止。
代码是按照任务的等待时间大小来执行的,不是按照任务创建的先后执行的。