目录
定时器
定时器是软件开发中的一个重要组件,类似于一个闹钟,达到一个特定时间后,就执行某个指定好的代码。
标准库中的定时器
· 标准库提供了一个Timer类,Timer类的核心方法为schedule.
· schedule方法包含两个参数,第一个参数指定将要执行的任务代码,第二个参数指定多长时间后执行这段代码(单位为毫秒).
定时器的使用
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Hello");
}
},3000);
}
}
上述代码的意思为TimerTask对象为所要执行的任务代码,指定为输出"Hello",后面的3000为指定的时间,即程序运行3000毫秒后执行这段代码 .
运行结果
实现一个定时器
定时器的构成
· 一个带优先级的阻塞队列
因为阻塞队列中的任务都有各自的执行时刻,最先执行的任务一定是执行时刻最小的任务。使用带优先级的优先队列就可以高效的把执行时刻最小的这个任务找出来.
· 队列中的每一个元素是一个Task对象
· Task中带有一个时间属性,队首元素就是即将执行的元素
· 同时有一个work线程一直扫描队首元素,看队首元素是否需要执行
核心接口schedule
Timer类提供的核心接口就是schedule接口,用于注册一个任务,并指定这个任务多长时间后执行
public void schedule(Runnable command, long after) {
Task task = new Task(command,after);
queue.offer(task);
synchronized(mailBox) {
mailBox.notify();
}
}
mailBox对象
mailBox对象是定时器中非常重要的一个成员,可以借助该对象的wait和notify来解决while(true)的忙等问题,具体应用在下方扫描器woker中
//避免worker线程出现忙等的情况
private Object mailBox = new Object();
扫描器worker线程
woker线程的作用是扫描队首元素,看其是否能够执行这个任务,如果不利用mailBox对象的wait方法,woker线程就会一直扫描阻塞队列队首位置,一秒钟扫描几万次,但在加上mailBox对象的wait方法后就可以解决这个忙等的问题了
public class MyTimer {
class Worker extends Thread {
@Override
public void run() {
while(true) {
try {
Task task = queue.take();
long curTime = System.currentTimeMillis();
if(task.time > curTime) {
//如果执行时间还未到,就将任务重新放入阻塞队列中
queue.put(task);
synchronized (mailBox) {
//指定等待时间
mailBox.wait(task.time - curTime);
}
}else {
//时间如果到了就执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public MyTimer() {
//启动work线程
Worker worker = new Worker();
worker.start();
}
}
完整代码
import java.util.concurrent.PriorityBlockingQueue;
/**
* Created with IntelliJ IDEA.
* Description: 实现一个定时器
* User: Li_yizYa
* Date: 2022—05—24
* Time: 15:54
*/
public class MyTimer {
static class Task implements Comparable<Task> {
private Runnable command;
private long time;
public Task(Runnable command, long time) {
this.command = command;
//time中存的是绝对时间,超过这个时间任务就需要被执行
this.time = System.currentTimeMillis() + time;
}
public void run() {
command.run();
}
@Override
public int compareTo(Task o) {
//谁的时间小谁排在前面
return (int)(time - o.time);
}
}
//核心结构: 一个带优先级的阻塞队列
private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
//避免worker线程出现忙等的情况
private Object mailBox = new Object();
class Worker extends Thread {
@Override
public void run() {
while(true) {
try {
Task task = queue.take();
long curTime = System.currentTimeMillis();
if(task.time > curTime) {
//如果执行时间还未到,就将任务重新放入阻塞队列中
queue.put(task);
synchronized (mailBox) {
//指定等待时间
mailBox.wait(task.time - curTime);
}
}else {
//时间如果到了就执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public MyTimer() {
//启动work线程
Worker worker = new Worker();
worker.start();
}
//schedule
public void schedule(Runnable command, long after) {
Task task = new Task(command,after);
queue.offer(task);
synchronized(mailBox) {
mailBox.notify();
}
}
}
代码测试
class MyTimerTest {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
Runnable command = new Runnable() {
@Override
public void run() {
System.out.println("我来了");
timer.schedule(this,3000);
}
};
timer.schedule(command,3000);
}
}