首先我们要清楚 实现定时器的话我们需要定时器有哪些功能?
1.有一个优先级队列存放要执行的任务(什么样的优先级?需要我们自己规定)
2.有一个线程不断检查任务是否到时间需要执行了,时间到了执行,时间没到再放回到队列里,继续检查。
3.可以注册任务,并且能存放进优先级队列
这里我们必须注意的问题:
两个线程之间读和放,有可能造成线程安全问题
在每次存放之后有一个极端情况,在上一个任务被判定时间不到的时间内,放一个新的任务之后线程立即调度走,可能遗漏这个线程的通知,导致这个任务无法被提醒
由于扫描线程一直在检查,可能,会出现忙等现象,造成CPU资源浪费。
下面附上一个实现的定时器案例,配上详细的注释,希望对你的学习有所帮助。
import java.util.concurrent.PriorityBlockingQueue; public class ThreadDemo10 { public static void main(String[] args) { MyTimer myTimer = new MyTimer(); myTimer.schedule(new Runnable() { @Override public void run() { System.out.println("时间到了,执行!"); } },3000); System.out.println("执行之前。");} } //定时器 定时器内部能够存放多个任务,需要考虑到线程安全问题 还有一个专门的线程来取任务 class MyTimer { private Object locker = new Object(); private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<MyTask>(); //schedule 方法注册任务 然后存放到队列中 public void schedule(Runnable runnable, long delay) { MyTask task = new MyTask(runnable,delay); queue.put(task); //每次插入之后都要唤醒一下扫描线程,防止遗漏 synchronized (locker){ locker.notify(); } } //创建一个扫描线程 public MyTimer(){ Thread t1 = new Thread(()->{ //一直不断地扫描 while(true){ try { MyTask task = queue.take(); long curTime = System.currentTimeMillis(); //比较一下时间到了吗 if(curTime < task.getTime()){ //时间没到,塞回去。 queue.put(task); //再在这里设置一个等待时间 synchronized (locker){ //等待时间到了唤醒 // 这里的wait是指的最大时间,在这个时间内 如果有notify的话 线程会继续执行,再次检查一遍。 locker.wait(task.getTime()-curTime); } } else{ //时间到了执行 task.run(); } } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t1.start(); } } class MyTask implements Comparable<MyTask> { //实现comparable接口 让程序以某种规则来可以进行比较 //任务需要干啥 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); } }