您的位置:首页 > 汽车 > 新车 > 北京市工程建设_济南网络优化推广_进一步优化落实_站内seo优化

北京市工程建设_济南网络优化推广_进一步优化落实_站内seo优化

2024/11/13 9:33:37 来源:https://blog.csdn.net/yc_xym/article/details/142623751  浏览:    关键词:北京市工程建设_济南网络优化推广_进一步优化落实_站内seo优化
北京市工程建设_济南网络优化推广_进一步优化落实_站内seo优化

定时器相当于一个“闹钟”,在日常生活中,我们需要闹钟的辅佐,在代码中,也经常需要“闹钟”机制(网络通信中经常需设定一个超时时间)。


一.定时器的使用

在Java标准库中,也停供了定时器的实现。

Timer类

Timer timer=new Timer();

timer类提供了一个重要的方法schedule()

需填写两个参数,用于安排任务,以及设定定时时间。

使用代码:

public class Demo1 {public static void main(String[] args) {Timer timer=new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}},3000);System.out.println("程序开始运行");}
}

定时器可以多个一起使用,安排任务的先后顺序取决于所设置的延迟时间

public class Demo1 {public static void main(String[] args) {Timer timer=new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello3");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello1");}},1000);System.out.println("程序开始运行");}
}

因hello1延迟时间短,所以hello1先打印,其次是hello2,最后hello3

二.定时器的实现

对于定时器来说 ,有以下几个要点:

1.创建类,描述一个要执行的任务是啥(任务的内容,任务的时间)

class MyTimerTask{private long time;private Runnable runnable;public MyTimerTask(Runnable runnable,long delay){this.runnable=runnable;this.time=System.currentTimeMillis()+delay;}public long getTime(){return time;}public void run(){runnable.run();}
}

time表示程序应该执行的时候,等于当前时间戳+延迟等待的时间。

2.管理多个任务,通过一定的数据结构,把多个任务存起来

首先,考虑使用怎样的数据结构储存。

按照我们的设想,应该是执行时间短的先执行,执行时间晚的在后面依次排队。

在学习数据结构时学到过大根堆小根堆的数据结构,与场景符合。

我们可以使用优先级队列,在队列中放入MyTimerTask类。

 private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();

但无法直接放入MyTimerTask类,需指定放入的方式(根据什么放入)

public int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}

通过比较时间,将MyTimerTask类放入队列。

实现schedule方法,将任务放入队列。

  public void schedule(Runnable runnable,long delay){MyTimerTask myTimerTask=new MyTimerTask(runnable,delay);queue.offer(myTimerTask);}

3.有专门的线程去执行这些任务

创建线程去取出,执行队列中的任务。

首先,应该判断队列是否为空

如果为空

则需阻塞等待任务被放入队列

如果不为空,则查看任务,判断当前的时间戳有没有超过任务的时间戳。

如果没有超过,那就将任务进行阻塞等待,并设定等待的时间(任务执行的时间-当前时间戳),并在添加任务进队列时唤醒此处的等待。

注:这么做可以让出cpu资源,若是中间有执行时间更短的任务插队进来,可以让出cpu进行调度

如果当前时间>=任务执行的时间,那么就执行任务,并在执行后取出任务

class MyTimer{private Object lock=new Object();PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();public MyTimer(){ Thread t=new Thread(()->{while (true) {synchronized (lock) {while (queue.isEmpty()) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}MyTimerTask current=queue.peek();if(System.currentTimeMillis()>=current.getTime()){current.run();queue.poll();}else{try {lock.wait(current.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}}});t.start();}

然后,再考虑线程安全,以上操作中,涉及写和执行的操作,应该加上锁运行。

完整代码如下:

import java.util.PriorityQueue;class MyTimer{private Object lock=new Object();PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();public MyTimer(){ Thread t=new Thread(()->{while (true) {synchronized (lock) {while (queue.isEmpty()) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}MyTimerTask current=queue.peek();if(System.currentTimeMillis()>=current.getTime()){current.run();queue.poll();}else{try {lock.wait(current.getTime()-System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}}});t.start();}public void schedule(Runnable runnable,long delay) {synchronized (lock) {MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);queue.offer(myTimerTask);lock.notify();}}}class MyTimerTask implements Comparable<MyTimerTask>{private long time;private Runnable runnable;public MyTimerTask(Runnable runnable,long delay){this.runnable=runnable;this.time=System.currentTimeMillis()+delay;}public long getTime(){return time;}public void run(){runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}
}

测试如下:

public static void main(String[] args) {MyTimer myTimer=new MyTimer();myTimer.schedule(()->{System.out.println("hello 3000");},3000);myTimer.schedule(()->{System.out.println("hello 2000");},2000);myTimer.schedule(()->{System.out.println("hello 1000");},1000);}


补充

为什么队列不使用PriorityBlockingQueue而是要自己加锁?

使用阻塞队列后,queue.take()操作可能会阻塞,wait()操作也可能会阻塞,此时就有了两把锁。

两把锁,多个线程,容易出现死锁的情况。

需精心控制加锁的顺序,代码编程的复杂的提高。

如果不使用阻塞队列,可以通篇使用一把锁


以上便是全部内容,如有不对,欢迎指正

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com