dede做的网站怎样去换模版,优秀软文营销案例,做视频网站的技能,个人主页页面设计目录
一、定时器
1.定时器概述
2.Java标准库提供的定时器类
3.定时器代码样例
二、实现
1.实现思路
2.代码实现
2.1纯享版
2.2注释版
3.代码解析(超详细)
3.1描述类MyTimerTask
①构造#xff1a;MyTimerTask#xff08;Runnable runnable, long delay#xff…目录
一、定时器
1.定时器概述
2.Java标准库提供的定时器类
3.定时器代码样例
二、实现
1.实现思路
2.代码实现
2.1纯享版
2.2注释版
3.代码解析(超详细)
3.1描述类MyTimerTask
①构造MyTimerTaskRunnable runnable, long delay
②排序compareTo(MyTimerTask o)
③另两个
3.2※定时器类MyTimer
①任务队列入队schedule(Runnable runnable, long delay)
②扫描线程|构造 MyTimer()
③线程安全问题处理
4.一般执行流程分析 一、定时器
1.定时器概述
定时器作为软件开发中的重要组件用于在特定的时间间隔内执行某些任务。类似于我们生活中的“闹钟”在关键时刻提醒我们开始工作。当然程序可不像人类一样有惰性该出手时就出手可不会拖拖拉拉能够阻止它的只有bug或它自己。
它的应用场景例如在网络通信中如果需要在500ms内没有收到数据时断开连接并尝试重新连接就需要使用定时器。另外定时器还可以用于定期执行一些维护任务例如清理缓存、备份数据等。
2.Java标准库提供的定时器类
java.util.Timer是Java提供的定时器标准类使用它可以在指定时间后执行某个任务。而TimerTask类则是保存这份任务的载体它存储任务的代码及执行的时间实现了Runnable接口。
Timer类的核心方法是schedule有两个参数一个参数指定即将要执行的任务代码第二个参数指定多长时间后执行单位为毫秒。
方法参数1参数2 public void schedule(TimerTask task, long delay) 将要执行的任务代码 指定多长时间后执行 单位为毫秒 //创建Timer对象Timer timer new Timer();//设置三秒后输出Hello timer!!timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(Hello timer!!);}}, 3000);
Timer对象实例化new后需要手动停止否则Timer创建的前台线程会持续工作直到调用cancel方法后结束工作。
方法参数 public void cancel() 无 3.定时器代码样例
多任务差时测试
import java.util.Timer;
import java.util.TimerTask;public class ThreadDemo3 {public static void main(String[] args) {Timer timer new Timer();timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(3000ms Hello timer!!);}}, 3000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(2000ms Hello timer!!);}}, 2000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(1000ms Hello timer!!);}}, 1000);}
} 二、模拟实现
1.实现思路
参考标准库中的定时器实现有如下构成
优先级队列按执行时间循序存储任务一个schedul方法进行入队一个task类描述任务一个持续运转的线程扫描到时间的任务并执行其他属性和方法如时间time属性构造方法
就像是一个业务窗口扫描线程预约的人任务排成队伍优先级队列随时有其他人根据预约时间执行时间先后插入入队其中业务窗口一直面对的都是其中最先预约的人队首而且还要等到人预约时间到了才办理相应的业务执行任务。 根据如上结构我们分别要实现两个类MyTimer和MyTimerTask。
MyTimer在调用构造方法时就要将扫描线程创建出来不断判断优先级队列中的队首任务是否到了执行时间到了就执行任务若优先级队列为空则阻塞等待入队方法schedule的调用。
MyTack用于描述任务需要time属性记录执行时间runnable对象存储任务runnable重写run方法同时想要成为优先级队列的成员必须是可比较的因此要实现Comparable接口和compareTo方法构造方法所需属性由MyTimer类的入队方法schedule提供。
2.代码实现
2.1纯享版
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;class MyTimerTask implements ComparableMyTimerTask {private long time;private Runnable runnable null;//构造方法public MyTimerTask(Runnable runnable, long delay) {this.time System.currentTimeMillis() delay;this.runnable runnable;}//执行方法执行runnable存的任务public void run() {runnable.run();}//用于获取任务执行时间public long getTime() {return this.time;}Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
}class MyTimer {private Thread thread null;private Object lock new Object();private PriorityQueueMyTimerTask queue new PriorityQueue();//构造方法public MyTimer() {thread new Thread(() - {while(true) {try {synchronized(lock) {if(queue.isEmpty()) {lock.wait();}MyTimerTask task queue.peek();long time System.currentTimeMillis();while(time task.getTime()) {lock.wait(task.getTime() - System.currentTimeMillis());time System.currentTimeMillis();task queue.peek();}queue.poll();task.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();}//入队方法一个参数指定即将要执行的任务代码第二个参数指定多长时间后执行单位为毫秒public void schedule(Runnable runnable, long delay) {synchronized(lock) {MyTimerTask task new MyTimerTask(runnable, delay);queue.offer(task);lock.notify();}}
}
2.2注释版
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;class MyTimerTask implements ComparableMyTimerTask {//执行时间private long time;//执行的任务private Runnable runnable null;//构造方法参数delay提供的是’相对时间‘runnable提供的是任务public MyTimerTask(Runnable runnable, long delay) {//执行时间 当前时间 绝对时间this.time System.currentTimeMillis() delay;this.runnable runnable;}//执行方法执行runnable存的任务public void run() {runnable.run();}//用于获取任务执行时间public long getTime() {return this.time;}//用于比较执行顺序Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
}class MyTimer {//扫描线程用于扫描任务队列执行到点的任务private Thread thread null;//锁private Object lock new Object();//优先级队列任务队列对任务按执行时间进行排序private PriorityQueueMyTimerTask queue new PriorityQueue();//构造方法public MyTimer() {//构造方法中创建线程构造后使扫描线程直接开工thread new Thread(() - {//while循环持续扫描while(true) {try {//加锁保障线程安全synchronized(lock) {//当队列为空时阻塞线程入队操作执行后解除阻塞if(queue.isEmpty()) {lock.wait();}//获取队首任务获取当前时间MyTimerTask task queue.peek();long time System.currentTimeMillis();//当前时间不到任务执行时间时阻塞线程等待while(time task.getTime()) {//设置极限等待时间到时间自动解除阻塞lock.wait(task.getTime() - System.currentTimeMillis());//等待期间可能又有任务入队且成为新的队首//而入队操作会解除阻塞这时候要重置time和队首数据再通过循环判断当前是否要执行time System.currentTimeMillis();task queue.peek();}//执行任务并从任务队列取出已执行任务queue.poll();task.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();}//入队方法一个参数指定即将要执行的任务代码第二个参数指定多长时间后执行单位为毫秒public void schedule(Runnable runnable, long delay) {synchronized(lock) {MyTimerTask task new MyTimerTask(runnable, delay);//入队queue.offer(task);//入队时为扫描线程解除阻塞lock.notify();}}
}
3.代码解析(超详细)
3.1描述类MyTimerTask
再看一眼MyTask的要求MyTack用于描述任务需要time属性记录执行时间runnable对象存储任务runnable重写run方法同时想要成为优先级队列的成员必须是可比较的因此要实现Comparable接口和compareTo方法构造方法所需属性由MyTimer类的入队方法schedule提供。
一共需要两个变量long类型time用于记录执行任务的时间Runnable类对象runnable存储任务。 四个方法构造方法MyTimerTask用于初始化time和runnablecompareTo重写方法用于比较run方法来执行任务getTime方法用于外界获取任务执行时间来判断是否执行。
①构造MyTimerTaskRunnable runnable, long delay
没什么好说的干脆利落的将参数赋给对应的成员变量。
参数方面考虑到代码实用性所以传的时相对时间也就是多少ms后执行任务因此赋值给time时才要加上当前时间。 //构造方法参数delay提供的是’相对时间‘runnable提供的是任务public MyTimerTask(Runnable runnable, long delay) {//执行时间 当前时间 绝对时间this.time System.currentTimeMillis() delay;this.runnable runnable;}
②排序compareTo(MyTimerTask o)
“return this.time - o.time” 意为时间较time大者调用compareTo返回正值反之返回赋值相等则返回0不明白的话可以复习一下.
这样重写是为了让任务队列队首是time最小即最先要执行的那个任务。 //用于比较执行顺序Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
③另两个 没必要再提略……
3.2※定时器类MyTimer
MyTimer在调用构造方法时就要将扫描线程创建出来不断判断优先级队列中的队首任务是否到了执行时间到了就执行任务若优先级队列为空则阻塞等待入队方法schedule的调用。
MyTimer使用的是优先级队列且要在全局都访问到所以要定义成全局变量。锁lock同理。 //扫描线程用于扫描任务队列执行到点的任务private Thread thread null;//锁private Object lock new Object();//优先级队列任务队列对任务按执行时间进行排序private PriorityQueueMyTimerTask queue new PriorityQueue();
剩下就是两个核心方法构造方法以及任务队列入队方法。
①任务队列入队schedule(Runnable runnable, long delay)
先从相对简单多的schedule方法开始。
逻辑很简单根据参数实例化任务描述类task并将其插入任务队列优先级队列queue中优先级队列会将插入的task按照执行时间从近到远排序方便后续执行任务。最后若扫描线程thread陷入阻塞即之前队列为空时或还未到任务执行时间时。
注意插入后队首可能会发生变化notify操作又会解除阻塞可能导致误判出现bug。 //入队方法一个参数指定即将要执行的任务代码第二个参数指定多长时间后执行单位为毫秒public void schedule(Runnable runnable, long delay) {synchronized(lock) {MyTimerTask task new MyTimerTask(runnable, delay);//入队queue.offer(task);//入队时为扫描线程解除阻塞lock.notify();}}
②扫描线程|构造 MyTimer()
预期效果不断扫描任务队列当队首任务可执行时自动执行不可执行时阻塞等待到点自动执行队列为空时线程阻塞等待。要避免“忙等”浪费资源。
首先thread扫描线程在构造第一时间就要创建开始“扫描”避免错漏或耽搁代码逻辑要在线程内部实现。 //构造方法public MyTimer() {//构造方法中创建线程构造后使扫描线程直接开工thread new Thread(() - {//while循环持续扫描while(true) {try {//加锁保障线程安全synchronized(lock) {//当队列为空时阻塞线程入队操作执行后解除阻塞if(queue.isEmpty()) {lock.wait();}//获取队首任务获取当前时间MyTimerTask task queue.peek();long time System.currentTimeMillis();//当前时间不到任务执行时间时阻塞线程等待while(time task.getTime()) {//设置极限等待时间到时间自动解除阻塞lock.wait(task.getTime() - System.currentTimeMillis());//等待期间可能又有任务入队且成为新的队首//而入队操作会解除阻塞这时候要重置time和队首数据再通过循环判断当前是否要执行time System.currentTimeMillis();task queue.peek();}//执行任务并从任务队列取出已执行任务queue.poll();task.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();}
因为代码过长且不美观我们将thread内每次循环的代码逻辑抠出来分析 synchronized(lock) {//1.当队列为空时阻塞线程入队操作执行后解除阻塞if(queue.isEmpty()) {lock.wait();}//2.获取队首任务获取当前时间MyTimerTask task queue.peek();long time System.currentTimeMillis();//3.当前时间不到任务执行时间时阻塞线程等待while(time task.getTime()) {//设置极限等待时间到时间自动解除阻塞lock.wait(task.getTime() - System.currentTimeMillis());//等待期间可能又有任务入队且成为新的队首//而入队操作会解除阻塞这时候要重置time和队首数据再通过循环判断当前是否要执行time System.currentTimeMillis();task queue.peek();}//4.执行任务并从任务队列取出已执行任务queue.poll();task.run();} 首先判断队列是否为空为空则阻塞。为空说明当前无任务要执行阻塞释放锁一方面可以节省系统资源另一方面不释放锁没法进行入队操作添加任务进而永久阻塞 获取队首和当前时间用变量单独存储。一方面减少代码冗余另一方面为下面while循环判断做准备while循环判断当前队首任务是否可以执行不可以则阻塞。直接使用wait()阻塞不好使用notify解锁但加上自动解除阻塞时间后就不需要在考虑额外notify的问题了。还有重置time和task是为了避免阻塞结束后队首发生变化而当前时间是一定变的。到达执行时间通过task调用run()执行任务并把执行过的任务从队列中剔除掉。这个任务一定是首先要被执行的那个队首最小。
外层while循环不断循环上述逻辑就达到了我们所有的预期效果不断扫描执行可执行任务。线程安全问题解决具体请往下看。
③线程安全问题处理
MyTimer类的实现是可能出现一些线程安全问题的那又是如何将这些问题解决的呢。
大部常规问题解决
读写操作一同出现时很容易出现线程安全问题上述MiTimer的代码实现中两个方法都同时涉及到了读写操作如schedule方法中涉及到了queue而PriorityQueue类型并非是线程安全的。
解决方法也很简单将两个方法涉及到queue的部分都用synchronized包裹起来。
schedule方法调用导致队首变化问题解决
当MyTimer代码逻辑执行到“时间不到任务执行时间时阻塞线程”时线程阻塞在等待期间可能schedule方法会被多次调用其中可能会出现改变queue队首的情况。
配合schedule中notify的解除阻塞操作在判断条件是“time task.getTime()”的情况下如果task和time尤其是task不发生变化就会导致循环判断的其实不是队首这个应该最先执行的任务。 //当前时间不到任务执行时间时阻塞线程等待while(time task.getTime()) {//设置极限等待时间到时间自动解除阻塞lock.wait(task.getTime() - System.currentTimeMillis());//等待期间可能又有任务入队且成为新的队首//而入队操作会解除阻塞这时候要重置time和队首数据再通过循环判断当前是否要执行time System.currentTimeMillis();task queue.peek();}
在阻塞后重置task和time就很好的解决了这一安全问题。
4.一般执行流程实例
public class ThreadDemo {public static void main(String[] args) {MyTimer timer new MyTimer();timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(3000ms Hello timer!!);}}, 3000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(2000ms Hello timer!!);}}, 2000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(1000ms Hello timer!!);}}, 1000);}
}
实例化MyTimer扫描线程启动。
MyTimer timer new MyTimer();
调用schedule方法添加任务实例化对应的任务对象并插入优先级队列自动排序如下。 timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(3000ms Hello timer!!);}}, 3000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(2000ms Hello timer!!);}}, 2000);timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(1000ms Hello timer!!);}}, 1000);
扫描线程不间断扫描开始时实列化MyTimer时由于队列为空而阻塞入队添加任务后解除阻塞。
判断队首是否需要执行由于1000ms后执行所以暂时阻塞至一秒后自动解除阻塞。
判断通过出队执行任务打印1000ms Hello timer!!
同理1000ms后打印2000ms Hello timer!!
又1000ms后打印3000ms Hello timer!! public MyTimer() {thread new Thread(() - {while(true) {try {synchronized(lock) {if(queue.isEmpty()) {lock.wait();}MyTimerTask task queue.peek();long time System.currentTimeMillis();while(time task.getTime()) {lock.wait(task.getTime() - System.currentTimeMillis());time System.currentTimeMillis();task queue.peek();}queue.poll();task.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();}
任务队列清空扫描线程阻塞等待下次调用schedule方法解除阻塞…… 博主是Java新人每位同志的支持都会给博主莫大的动力如果有任何疑问或者发现了任何错误都欢迎大家在评论区交流“ψ(∇´)ψ