我似乎无法弄清楚如何在 java 中制作一个简单的计时器。我需要它做的只是显示时间,真的。所以只是一个开始方法,它一直在计数,比如 0:00、0:01、0:02 等。我看过一些其他类似的论坛帖子,但所有代码对我的水平来说有点复杂理解;我是 java 的新手。但是做一个只执行这样一个基本功能的计时器应该不难吧?如果有人可以提供帮助,将不胜感激:)
原文由 Nadim 发布,翻译遵循 CC BY-SA 4.0 许可协议
我似乎无法弄清楚如何在 java 中制作一个简单的计时器。我需要它做的只是显示时间,真的。所以只是一个开始方法,它一直在计数,比如 0:00、0:01、0:02 等。我看过一些其他类似的论坛帖子,但所有代码对我的水平来说有点复杂理解;我是 java 的新手。但是做一个只执行这样一个基本功能的计时器应该不难吧?如果有人可以提供帮助,将不胜感激:)
原文由 Nadim 发布,翻译遵循 CC BY-SA 4.0 许可协议
8 回答6.4k 阅读
1 回答4.2k 阅读✓ 已解决
3 回答2.3k 阅读✓ 已解决
2 回答3.2k 阅读
2 回答3.9k 阅读
3 回答1.7k 阅读✓ 已解决
1 回答2k 阅读✓ 已解决
这并不难。但是,我要提醒您,我已经看到一些关于堆栈溢出的非常混乱的答案,在某些情况下是非常糟糕的编码习惯,所以要非常小心。首先让我回答这个问题。
如果程序员在实现计时器时犯的最大错误似乎是认为他们需要一些东西来跟踪当前时间。也就是说,他们编写了某种循环,每秒递增一个变量或类似的愚蠢事情。您无需编写代码来跟踪时间。函数
System.currentTimeMillis()
会为你做这件事,而且它做的相当准确。定时器代码将涉及许多程序员混淆的两个方面:
计算显示时间所需要做的就是记录计时器启动的时间:
稍后,当您想要显示时间量时,只需从当前时间中减去它即可。
程序员犯的最大错误是认为他们需要一个变量来保存当前时间,然后编写代码每秒递增该变量,例如他们维护的称为“elapsedSeconds”的东西。问题在于您可以安排每秒调用一次代码,但无法保证调用该代码的确切时间。如果系统繁忙,该代码可能会比第二个调用晚很多。如果系统非常繁忙(例如从有故障的磁盘中获取页面),它实际上可能会延迟几秒钟。使用 Thread.sleep(1000) 函数每秒循环的代码会发现错误会随着时间的推移而累积。如果 sleep 有一次返回延迟 300 毫秒,则该错误会混入您对现在几点的计算中。这完全没有必要,因为操作系统 _有一个功能可以告诉您当前时间_。
无论您每秒运行此代码、每秒运行 100 次还是每 3.572 秒运行一次,以上计算都是准确的。关键是
currentTimeMillis()
是时间的准确表示,无论何时调用此代码——这是一个重要的考虑因素,因为不能保证线程和计时器事件在特定时间是准确的。定时器的第二个方面是刷新显示。这将取决于您用来展示的技术。在 GUI 环境中,您需要安排绘制事件。您希望这些绘制事件在显示预期更改的时间之后立即发生。然而,这很棘手。您可以请求绘制事件,但可能有数百个其他绘制事件在您之前排队等待处理。
一种懒惰的方法是每秒安排 10 个绘制事件。因为时间的计算不依赖于在特定时间点被调用的代码,并且因为如果同时重新绘制屏幕也没有关系,这种方法或多或少地保证了显示的时间会在大约 1⁄10 秒内显示正确的时间。这似乎有点浪费,因为 10 次中有 9 次你在绘制屏幕上已经存在的内容。
如果你正在编写一个带有某种动画(比如游戏)的程序,它每秒刷新屏幕 30 次,那么你什么都不用做。只需将计时器显示调用合并到您的常规屏幕刷新中即可。
如果绘制事件代价高昂,或者如果您正在编写执行终端式输出的程序,则可以通过计算显示改变之前的剩余时间来优化事件的调度:
变量 timeTillNextDisplayChange 保存您需要等待的毫秒数,直到计时器的秒数部分发生变化。然后,您可以安排一个绘画事件在那个时候发生,可能会调用
Thread.sleep(timeTillNextDisplayChange)
并在睡眠之后执行输出。如果您的代码在浏览器中运行,您可以使用此技术在正确的时间更新页面 DOM。请注意,此显示刷新计算不会影响计时器本身的准确性。线程可能会晚 10 毫秒甚至 500 毫秒从睡眠中返回,并且定时器的准确性不会受到影响。每次通过时,我们都会从 currentTimeMillis 计算等待时间,因此一次被调用迟到不会导致以后的显示迟到。
这是准确计时器的关键。不要指望操作系统在您要求时准确地调用您的例程或发送绘画事件。当然,对于现代机器,操作系统通常反应灵敏且准确。这发生在您没有运行太多其他东西的测试情况下,并且计时器似乎可以工作。但是,在生产中,在罕见的压力情况下,您不希望您的计时器因为系统繁忙而“漂移”。