自定义任务类型
//自定义任务类型
class Mytask implements Comparable<Mytask>{
//任务内容
private Runnable runnable;
//任务啥时候执行 (使用毫秒时间戳来表示)
private long time;
public Mytask(Runnable runnable,long time){
this.runnable = runnable;
this.time = time;
}
//获取当前任务时间
public long getTime(){
return time;
}
public void run(){
runnable.run();
}
//比较方法
@Override
public int compareTo(Mytask o1){
return (int)(this.time-o1.time);
}
}
自定义定时器
class Mytimer{ //扫描线程 private Thread t = null; //优先级阻塞队列 来保存任务 //因为这里使用了优先级队列, 所以泛型类必须是能够比较的,所以要确定Mytask的比较关系 private PriorityBlockingQueue<Mytask> priorityQueue = new PriorityBlockingQueue<>(); public Mytimer(){ t = new Thread(() -> { while(true){ //取出队首元素,查看是否到时间了,如果到时间了就执行,如果没到就重新放回队列 中 try { Mytask task = priorityQueue.take(); long currentTime = System.currentTimeMillis(); //还没到时间 if(task.getTime() > currentTime){ priorityQueue.put(task); synchronized (this){ this.wait(task.getTime() - currentTime); } }else{ task.run(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } //schedule //第一个参数是执行的内容 第二个参数是任务多少秒后执行 public void schedule(Runnable runnable,long time){ Mytask mytask = new Mytask(runnable,time+System.currentTimeMillis()); priorityQueue.put(mytask); synchronized (this){ this.notify(); } } }
执行效果
public class Demo2{ public static void main(String[] args) { Mytimer mytimer = new Mytimer(); mytimer.schedule(new Runnable() { @Override public void run() { System.out.println("任务1执行"); } },2000); mytimer.schedule(new Runnable() { @Override public void run() { System.out.println("任务2执行"); } },3000); } }
极端情况:
极端情况
在执行完画红线的代码后,此线程正好被cpu调度走了,这时候有新任务加入队列,执行schedule方法,而此时并没有wait方法,所以这里的notify没有任何意义(当然也不报错),但是当这个新任务加入到新队列,上面的代码所算的时间仍然还是之前队首任务的时间差,这时候就有可能错过刚刚加入进来新的任务。比如现在是13:00,原队列第一个任务执行时间是14:00,等待时间是1h,加入的新任务执行时间是13:30,这时候等待时间还是1h,这个新任务就会被错过。
解决方法:
加大synchronized的范围,使其必须先wait 再 notify