定时器

类似于前面讲过的Runnable,schedule这个方法会记录时间,当前任务什么时候去执行.
现在虽然代码执行完了,但是线程并没有结束
Timer内部,有自己的线程.为了保证随时可以处理新安排的任务,这个线程会继续执行.并且这个线程是前台线程.这样就可以更好的说明timer中的任务是在timer内部线程执行的.
定时器的自行实现
分析:
首先一个定时器中是有很多个任务的,先要能够把一个任务给描述出来,在使用数据结构把多个任务组织起来
1.创建一个TimerTask这样的类,表示一个任务,这个任务就需要包含两方面
(1)任务的内容
(2)任务的实际执行时间
这个可以用时间戳出来表示,在schedule的时候,先获取到当前的系统的时间,在这个基础上,加上delay时间间隔.得到了真实要执行这个任务的时间
2.使用一定的数据结构,把多个TimerTask给组织起来
由于我们并不知道将来要创建多少个任务,所以这里使用List来进行组织,但是如果任务非常多,如何确定哪个任务何时能够执行呢?这样就需要搞一个线程,不停的对上述的List进行遍历,看这里的每个元素,是否到了时间,时间到就执行,时间不到,就跳过下一个.
但是注意,这个思路并不科学!!如果这些任务的时间都还为时尚早,在时间到达之前,此处的这个扫描线程就需要一刻不停的反复扫描.
优化如下
1.我们并不需要扫描所有的任务
只需要盯住,时间最靠前的任务即可,最早的任务时间还没到的话,其它的任务时间更不会到
遍历所有任务=>只关注一个
那么我们如何知道哪个任务是最靠前的?如何知道哪个任务是最小的?
堆/优先级队列
2.针对这一个任务的扫描,也不必一直反复执行
而是在获取到队首元素的时间之后,和当前的系统时间做一个差值,根据这个差值,来决定休眠/等待(不会消耗CPU资源的)的时间,在这个时间到来之前,不会重复进行扫描的,这样也就大幅度降低了扫描的次数.
此处"提高效率"不是缩短执行时间(毕竟,定时器的时间是固定那么多的),这样做只不过是减少了资源的利用率,避免了不必要的CPU浪费.
import java.util.PriorityQueue;
import java.util.Timer;
/**
* Created with IntelliJ IDEA
* Description:
* User: lenovo
* Date: 2023 -10 -08
* Time: 17:54
*/
//实现定时器
class MyTimerTask implements Comparable<MyTimerTask>{
//具体内容
private Runnable runnable;
//延时时间
private long time;
//构造方法
public MyTimerTask(Runnable runnable,int time){
this.runnable=runnable;
this.time=System.currentTimeMillis()+time;
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
@Override
public int compareTo(MyTimerTask o) {
//这里是怎么知道谁减谁的呢?很简单,不用记,去试一试,无非就是从小到大以及从大到小两种情况
return (int)(this.time-o.time);
}
}
class MyTimer{
//需要优先级队列
//但是注意了,能使用优先级队列的前提是,优先级队列中的元素是可比较的
PriorityQueue<MyTimerTask> priorityQueue=new PriorityQueue<>();
//需要TimerTask
//需要进行加锁
private Object locker =new Object();
//需要schedule方法
public void schedule(Runnable runnable,int delay) {
MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);
//在堆中存放元素
synchronized (locker) {
priorityQueue.offer(myTimerTask);
locker.notify();
}
}
//需要一个扫描线程
public MyTimer(){
Thread t=new Thread(()->{
try {
while(true){
synchronized (locker){
while(priorityQueue.isEmpty()){
locker.wait();
//continue;
}
//当前时间
long curtime=System.currentTimeMillis();
MyTimerTask myTimerTask=priorityQueue.peek();
if(curtime>=myTimerTask.getTime()){
myTimerTask.getRunnable().run();
priorityQueue.poll();
}else{
//1.这里使用sleep是不合适的,因为sleep在休眠的时候并不会释放锁
//会影响其他线程进行schedule
//2.不能提前中断sleep,假如以前最早的任务是13.20,现在是13.00,sleep20分钟
//但是现在有一个紧急任务,需要13.05去执行,此时就无法去中断sleep
//也就是说,要使用一个可以增加一个任务就中断休眠重新判定的休眠
//此时wait notify是最好的选择
//Thread.sleep(myTimerTask.getTime()-curtime);
//由于我们在这个线程中多次用到wait,所以我们直接让原本属于这里的try-catch
// 去包含整个线程的执行内容
locker.wait(myTimerTask.getTime()-curtime);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
public class Demo22 {
public static void main(String[] args) {
MyTimer myTimer=new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3");
}
},3000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 2");
}
},2000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 1");
}
},1000);
System.out.println("程序开始");
}
}
请一定要注意认真的去看代码中的注释!!!(我懒得挑出来总结了)