目录
1.定时器是什么
2.标准库中的定时器
3.实现定时器
3.1 MyTimer类
1.首先Timer 类提供的核⼼接⼝为 schedule, ⽤于注册⼀个任务, 并指定这个任务多⻓时间后执⾏.
2.Timer 实例中, 通过 PriorityQueue 来组织若⼲个 Task 对象. 通过 schedule 来往队列中插⼊⼀个个 Task 对象.
3.Timer 类中存在⼀个 worker 线程, ⼀直不停的扫描队⾸元素, 看看是否能执⾏这个任务.
3.2MyTimerTask类
3.3main类
1.定时器是什么
2.标准库中的定时器
标准库中提供了⼀个 Timer 类. Timer 类的核⼼⽅法为 schedule .
schedule 包含两个参数. 第⼀个参数指定即将要执⾏的任务代码, 第⼆个参数delay指定多⻓时间之后 执⾏ (单位为毫秒)
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3000");
}
},3000);
3.实现定时器
定时器的构成• ⼀个带优先级队列(不要使⽤ PriorityBlockingQueue, 容易死锁!)• 队列中的每个元素是⼀个 Task 对象.• Task 中带有⼀个时间属性, 队⾸元素就是即将要执⾏的任务• 同时有⼀个 worker 线程⼀直扫描队⾸元素, 看队⾸元素是否需要执⾏
3.1 MyTimer类
1.首先Timer 类提供的核⼼接⼝为 schedule, ⽤于注册⼀个任务, 并指定这个任务多⻓时间后执⾏.
public void schedule(Runnable runnable , long delay){
synchronized (locker){
MyTimerTask task = new MyTimerTask(runnable, delay);
queue.offer(task);//用于将指定元素插入到队列尾部,如果队列已满则返回 false
//添加新元素后,唤醒扫描线程的wait
locker.notify();
}
MyTimerTask task = new MyTimerTask(runnable, delay);创建了一个MyTimerTask 对象,用于表示一个任务。它接受两个参数:runnable和delay。
参数runnable:
是一个Runnable
对象,表示要执行的具体任务逻辑。你可以通过实现Runnable
接口并重写run()
方法来定义自己的任务逻辑。
参数delay:
是一个long
类型的值,表示任务的延迟时间。它指定了从当前时间开始,到任务执行时间的时间间隔(以毫秒为单位)。在构造MyTimerTask
对象时,会根据当前时间和延迟时间计算出任务的绝对执行时间,并保存在time
成员变量中。
2.Timer 实例中, 通过 PriorityQueue 来组织若⼲个 Task 对象. 通过 schedule 来往队列中插⼊⼀个个 Task 对象.
//负责扫描任务队列,执行任务的线程。
private Thread t = null;
// 任务队列
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
//弄一个锁对象
public Object locker = new Object();
public void schedule(Runnable runnable , long delay){
synchronized (locker){
MyTimerTask task = new MyTimerTask(runnable, delay);
queue.offer(task);//用于将指定元素插入到队列尾部,如果队列已满则返回 false
//添加新元素后,唤醒扫描线程的wait
locker.notify();
}
}
PriorityQueue:
是Java集合框架中的一个实现了优先级队列的类。它可以用于存储元素,并按照一定的优先级来访问和删除元素。
同时我们为了线程的安全性,创建了一个锁对象。
3.Timer 类中存在⼀个 worker 线程, ⼀直不停的扫描队⾸元素, 看看是否能执⾏这个任务.
public MyTimer(){
t = new Thread(()->{
//扫描线程就需要循环的反复的扫描队首元素,然后判定队首元素是不是时间到了
//如果没有到,啥也不干
//如果到了,就执行这个任务并且把这个任务从队列中删除掉。
while (true){
try {
synchronized (locker){
if(queue.isEmpty()){
//暂不处理
locker.wait();
}
MyTimerTask task = queue.peek();
//获取到当前时间
long curTime = System.currentTimeMillis();
if(curTime >= task.getTime()){
//当前时间已经到了任务时间,就可以执行任务了
queue.poll();//从队列中获取队头元素,并将其从队列中移除。
task.run();
}else {
//当前时间还没有到,暂时先不执行
//continue;//“忙等”,没有意义,应该把cpu资源让出来
//Thread.sleep 不能用sleep会错过新的任务
locker.wait(task.getTime() - curTime);
}
}//解锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
如果队列为空的话我们就暂时不出力,用wait 等待一下。我们用curtime来接受当前的时间。
3.2MyTimerTask类
class MyTimerTask implements Comparable<MyTimerTask>{
//在什么时间点来执行这个任务
//此约定这个 time 是一个 ms 级别的时间戳
private long time;
//实际任务要执行的代码
private Runnable runnable;
public long getTime(){
return time;
}
public MyTimerTask(Runnable runnable , long delay){
this.runnable = runnable;
//计算一下真正要执行任务的绝对时间(使用绝对时间,方便判定任务是否到达时间的)
this.time = System.currentTimeMillis() + delay;
}
public void run(){
runnable.run();
}
@Override
public int compareTo(MyTimerTask o) {
return (int) (this.time - o.time);
//return (int)(o.time - this.time);//试一试是哪一个减哪一个
}
}
private Runnable runnable:
我们将传入的runnable
参数保存到成员变量runnable
中。当任务到达执行时间时,定时器会调用MyTimerTask
对象的run()
方法来执行具体的任务逻辑,而run()
方法实际上就是调用runnable
对象的run()
方法。这样一来,我们就可以通过传入不同的Runnable
对象来定义不同的任务逻辑。
this.time = System.currentTimeMillis() + delay;
System.currentTimeMillis()
返回当前系统时间的毫秒表示,表示从1970年1月1日00:00:00 GMT开始到现在的时间间隔。delay
是任务的延迟时间,以毫秒为单位。通过将当前系统时间和延迟时间相加,我们可以得到任务的绝对执行时间。
3.3main类
public class ThreadDemo25 {
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 5000");
}
},5000);
System.out.println("hello main");
}
}
首先创建了一个 MyTimer
对象 timer
。然后,通过调用 timer.schedule()
方法来添加三个任务。每个任务都是通过匿名内部类实现的 Runnable
接口,并重写了 run()
方法在 run()
方法中,分别打印出不同的消息,表示任务的内容。每个任务都具有不同的延迟时间:3000毫秒、2000毫秒和5000毫秒。这意味着第一个任务将在程序启动后的3秒钟之后执行,第二个任务将在2秒钟之后执行,第三个任务将在5秒钟之后执行。
希望大家多多支持!!!