当前位置: 首页 > news >正文

Java多线程-----定时器(Timer)及其实现

目录

一.定时器简介:

二.定时器的构造方法与常见方法:

三.定时器的模拟实现:

思路分析:

代码实现:


在开发中,我们经常需要一些周期性的操作,例如每隔几分钟就进行某一项操作,这时候我们就需要去设置个定时器,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() {@Overridepublic 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() {@Overridepublic 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比较方法,按照时间的从小到大排序@Overridepublic 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)

结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C++ 异常
  • 基于树莓派的智能家居中控系统:集成Flask、HTML、JavaScript与MQTT协议的文心一言AI接入(代码示例)
  • c语言11天笔记
  • @SpringBootConfiguration重复加载报错
  • 层次分析法(评价类问题)
  • NLP——文本预处理
  • Vue脚手架的安装(超详细篇,保姆级教程)
  • 【web3.0】Web3 开发教程与代码资源:探索如何在Web3项目中开发应用
  • VBA之Excel应用第二章第三节:InputBox函数对话框
  • Io 35
  • VUE实现TAB切换不同页面
  • 【Vue】vue3 中使用 ResizeObserver 监听元素的尺寸宽度变化
  • 洛谷练习(8.6)
  • Maven实战.插件
  • 深入SpringBoot:SpringCache的集成与使用
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 【翻译】babel对TC39装饰器草案的实现
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • happypack两次报错的问题
  • java8-模拟hadoop
  • Laravel5.4 Queues队列学习
  • spark本地环境的搭建到运行第一个spark程序
  • 从0到1:PostCSS 插件开发最佳实践
  • 当SetTimeout遇到了字符串
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 机器学习 vs. 深度学习
  • 计算机常识 - 收藏集 - 掘金
  • 时间复杂度与空间复杂度分析
  • 通过git安装npm私有模块
  • 延迟脚本的方式
  • 一天一个设计模式之JS实现——适配器模式
  • k8s使用glusterfs实现动态持久化存储
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • 如何用纯 CSS 创作一个货车 loader
  • ​Redis 实现计数器和限速器的
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #HarmonyOS:基础语法
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (4)logging(日志模块)
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (WSI分类)WSI分类文献小综述 2024
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (原)本想说脏话,奈何已放下
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)项目管理杂谈-我所期望的新人
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .net core 的缓存方案
  • .NET delegate 委托 、 Event 事件
  • .NET 药厂业务系统 CPU爆高分析