1.标准库中的定时器
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到1");
}
}, 3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到2");
}
}, 4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到3");
}
}, 5000);
System.out.println("开始记时");
}
}
知识点:
- 调用schedule方法安排任务
第一个参数是要执行的任务,TimerTask实现了Runnable接口,咱们需要继承TimerTask,重写run方法。第二个参数表示多长时间之后执行,时间单位是ms。
- 一个定时器可以安排多个任务,底层使用堆来存放任务
- Timer内部有一组线程来执行这些任务,这些线程是前台线程,只有所有前台线程都结束了,进程才会退出
2.实现一个定时器MyTimer
Java中的Timer类实现原理是通过一个TimerTask类表示定时任务,该类中有一个延迟时间的属性,已经一个run方法,用于表示定时任务要执行的操作。使用TaskQueue来存储所有的TimerTask对象,并使用TaskThread线程扫描TaskQueue取出TimerTask.
class MyTask implements Comparable<MyTask>{
//要执行的任务
private Runnable runnable;
//什么时间执行任务
private long time;
public MyTask(Runnable runnable, long time) {
this.runnable = runnable;
this.time = System.currentTimeMillis() + time;
}
public long getTime() {
return time;
}
public Runnable getRunnable() {
return runnable;
}
@Override
public int compareTo(MyTask o) {
return (int) (this.time - o.time);
}
}
class MyTimer {
private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
Object locker = new Object();
//创建一个扫描线程,取出队列中的任务
public MyTimer() {
Thread t = new Thread(() -> {
while (true) {
try {
//1.使用wait需要先加锁
//2.多线程下,如果在t1线程执行到take之后,wait之前,这个任务要阻塞1小时,此时t2线程put一个任务
//需要半小时后执行,此时notify就不会唤醒wait,导致新任务无法执行,所以需要把下面代码都加锁
synchronized (locker) {
//取出任务
MyTask task = queue.take();
//判断任务是否达到执行时间
long curTime = System.currentTimeMillis();
if(curTime >= task.getTime()) {
//执行任务
task.getRunnable().run();
}else {
//没到执行时间,放回队列中
queue.put(task);
//等待到时间到
locker.wait(task.getTime() - curTime);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
public void schedule(Runnable runnable, long after) throws InterruptedException {
MyTask myTask = new MyTask(runnable, after);
queue.put(myTask);
synchronized (locker) {
locker.notify();
}
}
}