各位看官,大家早安午安晚安呀~~~
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦
今天我们来学习【Java并发编程】手把手教你实现定时器
1.Java提供的定时器的使用
public static void main(String[] args) {
Timer timer = new Timer(); //我们的定时器类
timer.schedule(new TimerTask() { // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
@Override
public void run() {
System.out.println("111");
}
},2000); // 相对于现在的时间延迟1s
System.out.println("主线程结束");
}
我们定时器是可以安排多个任务的
public static void main(String[] args) {
Timer timer = new Timer(); //我们的定时器类
timer.schedule(new TimerTask() { // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
@Override
public void run() {
System.out.println("111");
}
},1000); // 相对于现在的时间延迟1s
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(222);
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("333");
}
},3000);
System.out.println("主线程结束");
}
结果:
总结以下几点注意
1.绝对时间(我们后续比较都是按照绝对时间进行比较,最好在MyTimerTask构造方法里面构造好绝对时间)
2.我们的定时任务要可以进行比较(也可以给优先级队列传一个实现了Comparator接口的类对象(按时间比较MyTimerTask))
3.本身就在多线程这个队列(这个队列不安全,我们本来就要加锁),put和take要加锁别忘了当时(直接再while循环外边的话可以避免频繁地获取锁,更高效一点)
4.非空的时候我们才能拿到队首元素(空了进行等待)
5.到点了运行完成记得poll出队列
6.如果没到运行时间(就别一直循环吃cpu资源了,直接原地等到到了时间(计算的相对时间,这个相对时间可不是我们传的相对时间是根据当前的时间计算的),notify唤醒的时候,唤醒哪一个我们都可以再走一遍循环(更新一下时间信息))
2.手撕定时器底层实现
package TimerDemo1;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Timer;// 在UTil包里面
import java.util.TimerTask;
class MyTimerTask implements Comparable<MyTimerTask> { // 我们的任务的时间应该能够比较,我们使用优先级队列把我们的任务串联起来
private Runnable runnable; // 我们的具体的任务是干哈的
private long delay; // 延迟的时间
private long time; // 这个任务的发生的绝对时间,一定要是私有的,别人不能更改
public MyTimerTask(Runnable runnable , long delay){
this.runnable = runnable;
this.time = System.currentTimeMillis() + delay;
}
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.time - o.time);//记得强转,表达式两个括号
}
public long getTime() {
return time;
}
public Runnable getRunnable() {
return runnable;
}
}
public class MyTimer {
Object object = new Object();
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
public void schedule(Runnable runnable , long delay){
synchronized (object){
queue.offer(new MyTimerTask(runnable,delay));
// 刚才队列空了要等待,我们这里放进去元素要给它唤醒
object.notify(); // 我们打断哪一个wait都可以,要么是要放进来任务了(都是能再走一遍流程)
// notify记得加锁呀
}
}
public MyTimer(){
Thread st = new Thread(() ->{// 构造方法里面有一个扫描线程
// 这个锁的位置更高效
synchronized (object) { // 本身就在多线程这个队列(这个队列不安全,我们本来就要加锁),put和take要加锁别忘了当时
while(true) { //直接开始就是不停的扫描
// 我们直接拿队首元素,一直比较看看到时间了没有
// 前提的我们有队首元素
try {
while (queue.isEmpty()) { //如果队列为空我们就等待别的线程放进来任务(while)
object.wait();
}
MyTimerTask task = queue.peek(); // 不能poll,我们只是拿队首元素看一下到没到时间
if (System.currentTimeMillis() >= task.getTime()) {
task.getRunnable().run(); // 到点了执行
queue.poll(); // 记得poll呀
} else { // 也不能就干等着把(这也不是干等着,我们一直循环一直吃着cpu呢) 我们这里应该wait
object.wait(task.getTime() - System.currentTimeMillis()); // 等待的时间
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
st.start();
}
}
class Demo1 {
public static void main(String[] args) {
MyTimer timer = new MyTimer(); //我们的定时器类
timer.schedule(new Runnable() { // 我们可以看到我们的进程并没有结束,我们的定时器还在等待其他的线程过来加任务
public void run() {
System.out.println("111");
}
}, 1000); // 相对于现在的时间延迟1s
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println(222);
}
}, 2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("333");
}
}, 3000);
System.out.println("主线程结束");
}
}
上述就是【Java并发编程】手把手教你实现定时器的全部内容了。定时器的出现,我们可以提前部署很多我们需要的程序,一个很好的工具~~~
能看到这里相信您一定对小编的文章有了一定的认可。
有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~