目录
一.定时器简介:
二.定时器的构造方法与常见方法:
三.定时器的模拟实现:
思路分析:
代码实现:
在开发中,我们经常需要一些周期性的操作,例如每隔几分钟就进行某一项操作,这时候我们就需要去设置个定时器,Java中最方便,最高效的实现方式是用java.util.Timer工具类,在通过调度java.util.TimerTask任务来完成
一.定时器简介:
①.Timer是一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者是定期的重复执行,实际上是个线程,定时调度所拥有的TimerTask任务
②.TimerTask是一个抽象类,它的子类由Timer安排为一次执行或者重复执行的任务,实际上就是一个拥有run方法的类,需要定时执行的代码放到run方法体内.
二.定时器的构造方法与常见方法:
①.构造方法:
②.常用方法:
注意事项:
①.上述方法中TimerTask是一个抽象方法,其子类是一个可以被Timer执行的任务,要执行的代码放在run()方法体内实现
②.schedule()与scheduleAtFixedRate()的区别? 首先schedule(TimerTask task,Date time)与schedule(TimerTask task,long delay)都只是单次执行操作,并不存在多次调用任务的情况,所以没有提供scheduleAtFixedRate方法的调用方式。它们实现的功能都一样,那区别在哪里呢? (1)schedule()方法更注重保持间隔时间的稳定:保障每隔period时间可调用一次。
(2)scheduleAtFixedRate()方法更注重保持执行频率的稳定:保障多次调用的频率趋近于period 时间,如果某一次调用时间大于period,下一次就会尽量小于period,以保障频率接近于period。
③.每一个Timer仅对应一个线程,而不是每调用一次schedule就创建一个线程
④.Timer是线程安全的
代码实例1:
import java.util.Timer;
import java.util.TimerTask;
public class Mian {
public static void main(String[] args) throws InterruptedException {
//创建定时器线程对象同时设置名字
Timer timer = new Timer("定时器线程");
//传入要执行的任务,同时设置等待的时间
timer.schedule(new TimerTask() {
@Override
public void run() {
String current = Thread.currentThread().getName();
System.out.println(current + " : 任务代码的执行区域~~~");
}
},2000);
Thread.sleep(3000);
System.out.println("执行结束");
//结束定时器线程
timer.cancel();
}
}
运行结果:
代码实例2:
import java.util.Timer;
import java.util.TimerTask;
public class Mian2 {
public static void main(String[] args) throws InterruptedException {
//创建定时器线程对象同时设置名字
Timer timer = new Timer("定时器线程");
//传入要执行的任务,同时设置等待的时间
timer.schedule(new TimerTask() {
@Override
public void run() {
String current = Thread.currentThread().getName();
System.out.println(current + " : 任务代码的执行区域~~~");
}
},1000,2000);
Thread.sleep(6000);
//结束定时器线程
timer.cancel();
}
}
运行结果:
三.定时器的模拟实现:
在了解了什么是定时器和定时器的使用之后,那么定时器是如何实现的呢?这里我们通过模拟实现定时器,来进一步加深对定时器的理解。注:这里我们仅仅模拟实现Timer类不带参数的构造方法和等待delay时间后要执行的任务类,以及核心方法schedule。
思路分析:
Timer类通过schedule添加等待delay时间后执行的代码,此时的任务可能不止一个,我们需要一个容器来存放这些任务,同时,为了公平起见,我们让先到达指定时间的任务优先执行,很自然的我们可以想到用优先级队列来存储这些任务,队首元素就是最先执行的任务.
同时,我们也需要一个线程来扫描队首元素,判断队首元素是否是需要执行的任务
综上,我们自己模拟实现的定时器需要完成以下任务:
①.用一个优先级队列存放要执行的任务,队首元素是最先执行的任务
②.任务中带有时间属性,记录任务所要执行的时间
③.用一个线程来扫描队首元素,判断队首元素是否需要执行
④.这里出现多个线程同时操作共享数据的代码,我们要解决线程安全问题
代码实现:
import java.util.*;
class MyTimerTask implements Comparable<MyTimerTask>{
//要执行的任务代码
private Runnable runnable;
//ms级别的时间戳
private long time;
public MyTimerTask(Runnable runnable,long delay){
this.runnable = runnable;
//计算要执行的相对时间:当前时间+等待时间
this.time = System.currentTimeMillis() + delay;
}
public void run(){
runnable.run();
}
public long getTime(){
return time;
}
//重写compareTo比较方法,按照时间的从小到大排序
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.time - o.time);
}
}
//模拟实现定时器
class MyTimer{
private PriorityQueue<MyTimerTask> q = new PriorityQueue<>();
//锁对象
private static Object loker = new Object();
//构造方法中启动线程,让线程进行判定与执行
public MyTimer(){
Thread t = new Thread(()->{
try{
while(true){
//将操作共享数据的队列锁起来,一次只允许一个线程进行操作,避免线程安全问题
synchronized (loker){
if(q.isEmpty()){
loker.wait();
}
MyTimerTask current = q.peek();
//如果当前时间超过(>=) 设定的时间,此时需要执行任务
if(System.currentTimeMillis() >= current.getTime()){
current.run();
//执行完成后,将任务从队列中删除
q.poll();
}else{
//否则不执行任务
loker.wait(current.getTime() - System.currentTimeMillis());
}
}
}
}catch (InterruptedException e) {
e.printStackTrace();
}
});
//开启线程
t.start();
}
public void schedule(Runnable runnable,long delay){
//将操作共享数据的队列锁起来,一次只允许一个线程进行操作,避免线程安全问题
synchronized (loker){
MyTimerTask myTimerTask = new MyTimerTask(runnable,delay);
q.offer(myTimerTask);
loker.notify();
}
}
}
//测试
public class Demo {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.schedule(()->{
System.out.println("hello Thread" + ",3000 " + Thread.currentThread().getName());
},3000);
myTimer.schedule(()->{
System.out.println("hello Thread" + ",2000 " + Thread.currentThread().getName());
},2000);
myTimer.schedule(()->{
System.out.println("hello Thread" + ",1000 " + Thread.currentThread().getName());
},1000);
}
}
运行结果:
参考资料:
Java定时器的使用(Timer简介)_51CTO博客_java定时器
资源--timer的使用 - 牛李 - 博客园 (cnblogs.com)
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!