网站免费软件,网站地图 设计,怎么做北京赛车网站,html5手机网页模板文章目录 #x1f334;定时器是什么#x1f38b;Java标准库中的定时器#x1f332;模拟实现定时器#x1f6a9;定时器的构成#x1f4cc;第一步#xff1a;MyStack类的建立#x1f4cc;第二步#xff1a;创建MyTimer类#x1f4cc;第三步#xff1a;解决相关问题 定时器是什么Java标准库中的定时器模拟实现定时器定时器的构成第一步MyStack类的建立第二步创建MyTimer类第三步解决相关问题 完整代码实现与测试⭕总结 定时器是什么 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除). 类似于这样的场景就需要用到定时器. Java标准库中的定时器 标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule . schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒)
代码示例
下面程序分别有一个定时器设置了三个不同的时间
import java.util.Timer;
import java.util.TimerTask;public class TestDemo {public static void main(String[] args) {Timer timer new Timer();System.out.println(程序启动!);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(定时器3);}},3000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(定时器2);}},2000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(定时器1);}},1000);}
}运行结果如下 结果如我们所示按照时间顺序进行打印
模拟实现定时器
首先我们先来看一下定时器的构成
定时器的构成
一个带优先级的阻塞队列
为啥要带优先级呢? 因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来. 队列中的每个元素是一个 MyTask 对象. MyTask 中带有一个时间属性, 队首元素就是即将要执行的任务 同时有一个线程一直扫描队首元素, 看队首元素是否需要执行
第一步MyStack类的建立
包含两个属性 包含一个 Runnable 对象 一个 time(毫秒时间戳)
由于我们的MyTask类需要放入一个带优先级的阻塞队列中所以我们需要MyTack可以比较这里博主选择重写 Comparable 接口里的compareTo方法
代码实现如下
public class MyTask implements ComparableMyTask {private Runnable runnable;private long time;public MyTask() {System.out.println(1);}public void tad() {System.out.println(2);}public MyTask(Runnable runnable, long time) {this.runnable runnable;this.time time;}public long gettime(MyTask) {return this.time;}//执行任务public void run() {runnable.run();}Overridepublic int compareTo(MyTask o) {return (int)(this.time - o.time);}
}第二步创建MyTimer类
该类需要有一个带有优先级的阻塞队列
还需要有一个可schedule 方法用于我们来插入我们我们需要执行的任务
public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueueMyTask queue new PriorityBlockingQueue();// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task new MyTask(runnable, System.currentTimeMillis() after);queue.put(task);}
}由于我们输入的都是一个时间大小所以我们需要进行处理一下 这里的System.currentTimeMillis()是获取当时的时间戳 再加上所需要的时间大小即达到我们效果
其次还需要一个线程循环扫描 该扫描我们需要做的是 取出队首元素, 检查看看队首元素任务是否到时间了. 如果时间没到, 就把任务塞回队列里去. 如果时间到了, 就把任务进行执行. private Thread t null;public MyTimer() {t new Thread() {Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.MyTask myTask queue.take();long curTime System.currentTimeMillis();if (curTime myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行//塞回去queue.put(myTask);} else {// 时间到了!! 执行任务!!myTask.run();}} catch (InterruptedException e) {e.printStackTrace();}}}};//启动线程t.start();}
第三步解决相关问题
问题一while (true) 转的太快了, 造成了无意义的 CPU 浪费.
比如第一个任务设定的是 1 min 之后执行某个逻辑. 但是这里的 while (true) 会导致每秒钟访问队首元素几万次.
解决办法引入一个locker对象, 借助该对象的 wait / notify 来解决 while (true) 的忙等问题.
我们在循环扫描里引入 wait, 等待一定的时间.并修改 MyTimer 的 schedule 方法, 每次有新任务到来的时候唤醒一下循环扫描线程. (因为新插入的任务可能是需要马上执行的)
问题二原子性问题
由于我们的出队列操作和判断语句不具有原子性
问题情况如下
出队列操作拿到任务后还没有进行判断
然后这时候有一个来了一个新任务 但是此时我们该任务还没有wait()操作而且我们由于添加新元素notify()操作已执行这就导致后面的wait操作不会被唤醒那么新来的任务就在相应时间来没有被执行
解决方法将出队列操作与判断操作都加上锁
代码实现如下
import java.util.concurrent.PriorityBlockingQueue;public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueueMyTask queue new PriorityBlockingQueue();// 扫描线程private Thread t null;private Object locker new Object();public MyTimer() {t new Thread() {Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.synchronized (locker) {MyTask myTask queue.take();long curTime System.currentTimeMillis();if (curTime myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行queue.put(myTask);// 在 put 之后, 进行一个 waitlocker.wait(myTask.getTime() - curTime);} else {// 时间到了!! 执行任务!!myTask.run();}}} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();}// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task new MyTask(runnable, System.currentTimeMillis() after);queue.put(task);synchronized (locker) {locker.notify();}}
}完整代码实现与测试
计时器完整代码
import java.util.concurrent.PriorityBlockingQueue;class MyTask implements ComparableMyTask {private Runnable runnable;private long time;public MyTask() {System.out.println(1);}public void tad() {System.out.println(2);}public MyTask(Runnable runnable, long time) {this.runnable runnable;this.time time;}public long getTime() {return this.time;}//执行任务public void run() {runnable.run();}Overridepublic int compareTo(MyTask o) {return (int)(this.time - o.time);}
}public class MyTimer {// 有一个阻塞优先级队列, 来保存任务.private PriorityBlockingQueueMyTask queue new PriorityBlockingQueue();// 扫描线程private Thread t null;private Object locker new Object();public MyTimer() {t new Thread() {Overridepublic void run() {while (true) {try {// 取出队首元素, 检查看看队首元素任务是否到时间了.// 如果时间没到, 就把任务塞回队列里去.// 如果时间到了, 就把任务进行执行.synchronized (locker) {MyTask myTask queue.take();long curTime System.currentTimeMillis();if (curTime myTask.getTime()) {// 还没到点, 先不必执行// 现在是 13:00, 取出来的任务是 14:00 执行queue.put(myTask);// 在 put 之后, 进行一个 waitlocker.wait(myTask.getTime() - curTime);} else {// 时间到了!! 执行任务!!myTask.run();}}} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();}// 指定两个参数// 第一个参数是 任务 内容// 第二个参数是 任务 在多少毫秒之后执行. 形如 1000public void schedule(Runnable runnable, long after) {// 注意这里的时间上的换算MyTask task new MyTask(runnable, System.currentTimeMillis() after);queue.put(task);synchronized (locker) {locker.notify();}}
}测试代码如下
public class TestDemo2 {public static void main(String[] args) {MyTimer myTimer new MyTimer();System.out.println(程序启动);myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(计时器3);}},3000);myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(计时器2);}},2000);myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(计时器1);}},1000);}
}测试结果如下
⭕总结
关于《【JavaEE初阶】 定时器详解与实现》就讲解到这儿感谢大家的支持欢迎各位留言交流以及批评指正如果文章对您有帮助或者觉得作者写的还不错可以点一下关注点赞收藏支持一下