文章目录
- 1. 定时器是什么?
- 2. 定时器的应用
- 3. 自己实现定时器
1. 定时器是什么?
定时器就类似生活中的闹钟,它是软件开发中的一个重要组件。当有些线程我们并不希望它立刻执行,这个时候我们就可以使用定时器,规定线程在多长时间后执行。
2. 定时器的应用
定时器在Java标准库中提供的有一个Timer类。Timer中核心的方法是schedule,schedule中有俩个参数,一个是参数是执行任务代码,另一个参数是指定代码多少毫秒后执行。
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3秒!");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2秒");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("1秒");
}
},1000);
System.out.println("开始");
}
}
运行结果:
3. 自己实现定时器
自己实现一个定时器能够帮助我们更好的理解定时器。实现定时器之前我们要知道定时器的构成。
- 一个带优先级的堵塞队列;(后面说为什么使用优先级堵塞队列)
- 队列中元素都是一个个要执行的任务;
- 队列属性要带有时间属性;
- 同时有个线程不停扫描队列元素,找到要执行的任务。
为什么使用优先级堵塞队列?
如果使用普通队列:在执行任务前,扫描线程不停的遍历队列,找到要执行的任务,在遍历时,线程执行的速度时非常快的,这个时候就非常浪费CPU资源。
使用优先级堵塞队列:那么,我们的扫描线程只需要扫描队列的队头,因为 在这个优先级堵塞队列中可以使最先执行的任务放在队头,而且在扫描到这个对头后,我们也可以进入休眠,更一步减少资源浪费。
class MyTimerTask implements Comparable<MyTimerTask>{
//TimerTask的两个参数
private Runnable runnable;
private long time;
//实例化
public MyTimerTask(Runnable runnable, long 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) (time - o.time);
}
}
class MyTimer{
//带优先级的堵塞队列
PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
//锁
Object locker = new Object();
//实现Timer类中的schedule的方法,接收任务和时间两个参数
public void schedule(Runnable runnable, long time){
//多线程环境,对队列操作都加锁,保证线程安全
synchronized (locker) {
//任务时间
MyTimerTask myTimerTask = new MyTimerTask(runnable, time);
//加入到队列
queue.offer(myTimerTask);
//每次添加元素,都唤醒一次循环队列,防止超时
locker.notify();
}
}
public MyTimer(){
//在实例化一个MyTimer,那么就就会启动这个扫描线程,进行扫描
Thread t = new Thread(() -> {
//使用循环,扫描线程并不是只执行一次
while (true) {
try {
//加锁,保证线程安全
synchronized (locker) {
//使用while,不要使用if,防止出现非线程安全
while (queue.isEmpty()) {
//队列为空,没有要执行的任务,进行等待,堵塞
locker.wait();
}
//取到队列对头,但不出队列,并不一定到时间执行
MyTimerTask myTimerTask = queue.peek();
//现在的时间
long time = System.currentTimeMillis();
//现在的时间如果大于任务要执行的时间,队头便出队列
if (time >= myTimerTask.getTime()) {
queue.poll();
//执行任务
myTimerTask.getRunnable().run();
} else {
//如果不到时间,进行等待,计算时间差,防止还没到就被唤醒,带来资源浪费
locker.wait(myTimerTask.getTime() - time);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//启动!!!
t.start();
}
}
public class Test3 {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("3秒!");
}
},3000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("2秒");
}
},2000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("1秒");
}
},1000);
System.out.println("开始!");
}
}
运行结果: