网站首页 > 技术教程 正文
定时器已经是现代软件中不可缺少的一部分,例如每隔5秒去查询一下状态,是否有新邮件,实现一个闹钟等, Java 中已经有现成的 api 供使用,但是如果你想设计更高效,更精准的定时器任务,就需要了解底层的硬件知识,比如实现一个分布式任务调度中间件,你可能要考虑到各个应用间时钟同步的问题。
Java 中我们要实现定时任务,有两种方式,一种通过 timer 类, 另外一种是 JUC 中的 ScheduledExecutorService ,不知道大家有没有好奇 JVM 是如何实现定时任务的,难道一直轮询时间,看是否时间到了,如果到了就调用对应的处理任务,但是这种一直轮询不释放 CPU 肯定是不可取的,要么就是线程阻塞,等到时间到了在来唤醒线程,那么 JVM 怎么知道时间到了,如何唤醒呢?
首先我们翻一下 JDK ,发现和时间相关的 API 大概有3处,而且这 3 处还都对时间的精度做了区分:
object.wait(long millisecond) 参数是毫秒,必须大于等于 0 ,如果等于 0 ,就一直阻塞直到其他线程来唤醒 ,timer 类就是通过 wait() 方法来实现,下面我们看一下wait的另外一个方法:
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
这个方法是想提供一个可以支持纳秒级的超时时间,然而只是粗暴的加 1 毫秒。
Thread.sleep(long millisecond) 目前一般通过这种方式释放 CPU ,如果参数为 0 ,表示释放 CPU 给更高优先级的线程,自己从运行状态转换为可运行态等待 CPU 调度,他也提供了一个可以支持纳秒级的方法实现,跟 wait 额区别是它通过 500000 来分隔是否要加 1 毫秒。
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
LockSupport.park(long nans) Condition.await()调用的该方法, ScheduledExecutorService 用的 condition.await() 来实现阻塞一定的超时时间,其他带超时参数的方法也都通过他来实现,目前大多定时器都是通过这个方法来实现的,该方法也提供了一个布尔值来确定时间的精度。
System.currentTimeMillis() 以及 System.nanoTime() 这两种方式都依赖于底层操作系统,前者是毫秒级,经测试 windows 平台的频率可能超过 10ms ,而后者是纳秒级别,频率在 100ns 左右,所以如果要获取更精准的时间建议用后者好了,api 了解完了,我们来看下定时器的底层是怎么实现的,现代PC机中有三种硬件时钟的实现,他们都是通过晶体振动产生的方波信号输入来完成时钟信号同步的。
实时时钟 RTC ,用于长时间存放系统时间的设备,即使关机也可以依靠主板中的电池继续计时。Linux 启动的时候会从 RTC 中读取时间和日期作为初始值,之后在运行期间通过其他计时器去维护系统时间。
可编程间隔定时器 PIT ,该计数器会有一个初始值,每过一个时钟周期,该初始值会减1,当该初始值被减到0时,就通过导线向 CPU 发送一个时钟中断, CPU 就可以执行对应的中断程序,也就是回调对应的任务
时间戳计数器 TSC , 所有的 Intel8086 CPU 中都包含一个时间戳计数器对应的寄存器,该寄存器的值会在每次 CPU 收到一个时钟周期的中断信号后就会加 1 。他比 PIT 精度高,但是不能编程,只能读取。
时钟周期:硬件计时器在多长时间内产生时钟脉冲,而时钟周期频率为1秒内产生时钟脉冲的个数。目前通常为1193180。
时钟滴答:当PIT中的初始值减到0的时候,就会产生一次时钟中断,这个初始值由编程的时候指定。
Linux启动的时候,先通过 RTC 获取初始时间,之后内核通过 PIT 中的定时器的时钟滴答来维护日期,并且会定时将该日期写入 RTC,而应用程序的定时器主要是通过设置 PIT 的初始值设置的,当初始值减到0的时候,就表示要执行回调函数了,这里大家会不会有疑问,这样同一时刻只能有一个定时器程序了,而我们在应用程序中,以及多个应用程序之间有好多定时器任务,我们可以参考 ScheduledExecutorService 的实现。
猜你喜欢
- 2025-08-06 电脑维修高手必备!8个神奇DOS命令,自己动手不求人
- 2025-08-06 飞牛fnNAS搭建Web版Linux系统
- 2025-08-06 CentOS系统下磁盘实战操作命令
- 2025-08-06 Linux系统iptables配置持久化的技巧
- 2025-08-06 MP3情怀?开机5、6秒就能听歌的播放器,真的香
- 2025-08-06 Linux系统学习系列——Linux系统日志管理(上)
- 2025-08-06 国产NAS系统飞牛私有云fnOS推送0.8.27:新增UPS功能
- 2025-08-06 springboot优雅关机
- 2025-08-06 向日葵一款专业的远程控制软件
- 2025-08-06 向日葵对比ToDesk,谁才是解决远程开关机问题的NO.1?
你 发表评论:
欢迎- 08-06linux 和 windows文件格式互相转换
- 08-06谷歌 ChromeOS 已支持 7z、iso、tar 文件格式
- 08-06Linux下比较文件内容的6种方法
- 08-06文件格式及功能汇总
- 08-0610个Linux文件内容查看命令的实用示例
- 08-06Linux-如何区分不同文件类型
- 08-06Zabbix技术分享——监控windows进程资源使用情况
- 08-06Linux系统卡顿?学会ps命令这三招,轻松定位问题进程
- 最近发表
- 标签列表
-
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
本文暂时没有评论,来添加一个吧(●'◡'●)