目录
定时器
1. 定时器的概念
定时器是一种用于执行定时任务的工具,它可以设定一个任务在未来某个时刻运行,或者在固定时间间隔内重复执行。
定时器在我们生活中用途非常广泛
用途1:在网上购物,用户下单后,30 分钟内未支付则自动取消订单
用途2:智能手表上面的心率检测,就是固定间隔一段时间进行心率检测,生成心率图
2. 标准库中的定时器
在java标准库中,常用Timer类和TimeTask类来实现定时器功能
TimerTask类
用于表示可以由Timer调度执行的任务,通过重写run()方法让用户可以自定义任务
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("111");
}
};
如上代码,通过重写run方法来自定义任务
注意:TimerTask类是一个抽象类
Timer类
允许在指定延迟后执行任务或按固定时间间隔重复执行任务,这些功能依靠schedule( )方法实现
schedule( )方法核心作用:将任务添加到定时器的调度队列中,并根据指定的延迟或周期参数安排任务的执行时间
可以认为是告诉定时器:“在某个时间点或周期,执行这个任务”。
timer.schedule(task,1000);
使用schedule()方法规定延迟的时间
timer.schedule(task,1000,3000);
schedule(TimerTask task, long delay, long period)方法,第一次延迟1秒执行任务,后面每隔3秒执行一次(循环)
通过使用Timer类和TimeTask类来实现定时器功能
import java.util.Timer;
import java.util.TimerTask;
public class Demo_6 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("111");
}
};
TimerTask task1 = new TimerTask() {
@Override
public void run() {
System.out.println("222");
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("333");
}
};
timer.schedule(task,1000);
timer.schedule(task1,2000);
timer.schedule(task2,3000);
});
thread.start();
System.out.println("任务开始");
}
}
将所有的任务执行完后,定时器进入阻塞状态,当有新任务被添加进调度队列中,会被唤醒并执行任务
3. 模拟定时器
了解了定时器的功能,我们可以尝试着自己构建出一个定时器
整体方向
- Timer类中使用优先级队列,队列中的元素都是TimerTask对象
使用优先级队列可以实现定时器优先使用延迟时间少的任务
- 每一个TimerTask对象都带有一个时间属性,为了保证优先级,使用比较器给出比较依据
- 实现只要队列里面有任务就会自动实现任务,一直处于运行状态,没有就会处于阻塞状态
可以将一个线程放在Timer的构造器中(达到只要构建出Timer实例,就启动线程,那么就可以一直执行任务)
线程的功能:一直扫描队首元素, 看队首元素是否需要执行
为了实现阻塞功能,可以使用带有优先级的阻塞队列
MyTimerTask类的构建
在TimerTask类中需要构建出比较器(根据时间可以将任务进行排序,时间少的先执行)
每个任务都需要实现代办事项和延迟时间(构造方法中添加Runable和时间参数)
class MyTimeTask implements Comparable<MyTimeTask> {
private long time;
private Runnable runnable;
public MyTimeTask(Runnable runnable ,int delay){
this.runnable = runnable;
this.time = System.currentTimeMillis()+delay;
}
public long getTime(){
return time;
}
@Override
public int compareTo(MyTimeTask o) {
return (int) (this.time-o.time);
}
public void run() {
runnable.run();
}
}
System.currentTimeMillis()+delay:当前时间+延迟时间,得到的是一个绝对时间(具体什么时间执行)
MyTimer类的构建
schedule方法
调用schedule方法,传入具体实现的功能和延迟时间
在方法中:1. 创建出一个任务 2. 将任务添加进优先级队列中 3. 实现唤醒功能
PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();
Object lock = new Object();
//提供添加任务方法
public void schedule(Runnable runnable,int delay){
synchronized (lock){
MyTimeTask task = new MyTimeTask(runnable,delay);
queue.offer(task);
lock.notify();
}
}
MyTimer构造方法
- 当队列为空(没有任务),进入阻塞状态
- 当队列不为空(有任务),取出队列元素,判断是否可以执行,时间到了则执行,时间没有到,则进入阻塞状态
- 实现一直判断,在最外层套上while循环
public MyTimer(){
t = new Thread(()->{
//一直扫描
while (true) {
try {
synchronized (lock) {
//判断是否为空,为空,无法扫描
while (queue.isEmpty()) {//使用while,被唤醒后再次比较
// continue;//一直退出,然后在进来,容易导致线程饥饿问题
lock.wait();
}
//判断时间到了吗
MyTimeTask task = queue.peek();
long nowTime = System.currentTimeMillis();
//如果时间到了,那么就从队列中取出,并开始运行
if (nowTime >= task.getTime()) {
queue.poll();
task.run();
} else {
lock.wait(task.getTime()-nowTime);
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
出现了多个线程修改一个变量的情况,会出现线程安全问题(加锁)
点赞的宝子今晚自动触发「躺赢锦鲤」buff!