我似乎无法弄清楚如何在 java 中制作一个简单的计时器。我需要它做的只是显示时间,真的。所以只是一个开始方法,它一直在计数,比如 0:00、0:01、0:02 等。我已经看到了一些其他类似的论坛帖子,但所有代码对于我的水平来说都有点复杂理解; 我对java有点陌生。但是做一个只执行这样一个基本功能的计时器应该不难吧?如果有人可以提供帮助,将不胜感激:)
4 回答
这并不难。但是,我要提醒您,我在堆栈溢出方面看到了一些非常混乱的答案,在某些情况下,编码习惯非常糟糕,所以要非常小心。首先让我回答这个问题。
如果程序员在实现计时器时犯的最大错误似乎是认为他们需要一些东西来跟踪当前时间。也就是说,他们编写了某种循环,每秒增加一个变量或一些类似的傻事。您无需编写代码来跟踪时间。该功能System.currentTimeMillis()
将为您完成此操作,并且非常准确。
定时器代码将涉及许多程序员混淆的两个方面:
- 时间的计算
- 刷新显示
计算显示时间所需要做的就是记录计时器开始的时间:
long startTime = System.currentTimeMillis();
稍后,当您想要显示时间量时,只需从当前时间中减去它即可。
long elapsedTime = System.currentTimeMillis() - startTime;
long elapsedSeconds = elapsedTime / 1000;
long secondsDisplay = elapsedSeconds % 60;
long elapsedMinutes = elapsedSeconds / 60;
//put here code to format and display the values
程序员犯的最大错误是认为他们需要一个变量来保存当前时间,然后编写代码以每秒递增该变量,例如他们维护的称为“elapsedSeconds”的东西。问题是您可以安排每秒调用一次代码,但不能保证准确地调用该代码的时间。如果系统很忙,则调用该代码的时间可能会比第二个稍晚一些。如果系统非常繁忙(例如从故障磁盘中获取页面),它实际上可能会延迟几秒钟。使用 Thread.sleep(1000) 函数每秒循环的代码会发现错误会随着时间的推移而累积。如果 sleep 有一次延迟返回 300 毫秒,则该错误会复合到您计算现在的时间中。这都是完全没有必要的,因为操作系统具有告诉您当前时间的功能。
无论您每秒运行此代码、每秒运行 100 次还是每 3.572 秒运行一次,上述计算都是准确的。关键是currentTimeMillis()
无论何时调用此代码,它都是时间的准确表示——这是一个重要的考虑因素,因为不能保证线程和计时器事件在特定时间是准确的。
计时器的第二个方面是刷新显示。这将取决于您用于显示的技术。在 GUI 环境中,您需要安排绘制事件。您希望这些绘制事件在预期显示更改的时间之后立即出现。但是,这很棘手。您可以请求绘制事件,但可能有数百个其他绘制事件排队等待处理。
一种懒惰的方法是每秒安排 10 个绘制事件。因为时间的计算不依赖于在特定时间点被调用的代码,而且如果你用相同的时间重新绘制屏幕也没关系,这种方法或多或少保证了显示的时间在大约 1/10 秒内显示正确的时间。这似乎有点浪费,因为 10 次中有 9 次是在绘制屏幕上已有的内容。
如果您正在编写一个带有某种动画(如游戏)的程序,该程序每秒刷新屏幕 30 次,那么您无需执行任何操作。只需将计时器显示调用合并到您的常规屏幕刷新中即可。
如果绘制事件很昂贵,或者如果您正在编写一个执行终端样式输出的程序,您可以通过计算显示更改之前的剩余时间来优化事件的调度:
long elapsedTime = System.currentTimeMillis() - startTime;
long timeTillNextDisplayChange = 1000 - (elapsedTime % 1000);
变量 timeTillNextDisplayChange 保存您需要等待的毫秒数,直到计时器的秒部分发生变化。然后,您可以安排在那个时候发生绘制事件,可能会调用Thread.sleep(timeTillNextDisplayChange)
并在睡眠之后执行输出。如果您的代码在浏览器中运行,您可以使用此技术在正确的时间更新页面 DOM。
请注意,此显示刷新计算中没有任何内容会影响计时器本身的准确性。线程可能会延迟 10ms 甚至 500ms 从睡眠中返回,并且不会影响计时器的准确性。每次通过时,我们都会从 currentTimeMillis 计算等待时间,因此一次调用迟到不会导致稍后的显示迟到。
这是精确计时器的关键。不要指望操作系统会在您要求时调用您的例程或发送绘图事件。当然,通常,对于现代机器,操作系统的响应速度非常快且准确。这发生在您没有运行太多其他东西并且计时器似乎工作的测试情况下。但是,在生产中,在罕见的压力情况下,您不希望您的计时器因为系统繁忙而“漂移”。
为了根据您的需要创建一个简单的计时器,为此编写代码非常容易。我已经编写了以下代码供您参考。如果你愿意,你可以加强它。
导入 java.util.concurrent.TimeUnit;公共类 PerfectTimer {
public static void main(String[] args) throws InterruptedException
{
boolean x=true;
long displayMinutes=0;
long starttime=System.currentTimeMillis();
System.out.println("Timer:");
while(x)
{
TimeUnit.SECONDS.sleep(1);
long timepassed=System.currentTimeMillis()-starttime;
long secondspassed=timepassed/1000;
if(secondspassed==60)
{
secondspassed=0;
starttime=System.currentTimeMillis();
}
if((secondspassed%60)==0)
displayMinutes++;
System.out.println(displayMinutes+"::"+secondspassed);
}
}
}
您可以使用Timer
class fromjava.util
或其他更复杂的方式,即使用 Threads。Timer 也有线程动作,但使用起来很容易理解。
如果您想更新主线程中的某些内容(例如 UI 组件),最好使用 Handler
Handler h = new Handler();
h.postDelayed(new Runnable() {
@Override
public void run() {
//do something
}
}, 20);
20 - 在MS做某事的延迟。并循环运行。