我有一个奇怪的问题 - 我希望有人能向我解释发生了什么事以及可能的解决方法。我在Java中实现了一个Z80内核,并试图通过在单独的线程中使用java.util.Timer对象来减慢速度。Windows XP与Windows 7的Java计时准确性
基本设置是我有一个线程运行一个执行循环,每秒50次。在这个执行循环中,执行多个循环,然后调用wait()。外部定时器线程将每20ms调用Z80对象的notifyAll(),模拟3.54 MHz(ish)的PAL Sega主系统时钟频率。
我上面描述的方法在Windows 7上完美工作(尝试两台机器),但我也尝试过两台Windows XP机器,并且在它们两个上,Timer对象似乎都以大约50%左右的频率过度睡着。这意味着一秒钟的模拟时间实际上在Windows XP机器上花费大约1.5秒左右。
我曾尝试使用Thread.sleep()而不是Timer对象,但这具有完全相同的效果。我意识到大多数操作系统的时间粒度不会超过1毫秒,但我可以忍受999毫秒或1001毫秒,而不是1000毫秒。我无法忍受的是1562ms - 我只是不明白为什么我的方法可以在较新版本的Windows上正常工作,但不是旧版本 - 我已经研究了中断周期等,但似乎没有已经开发了一种解决方法。
任何人都可以请告诉我这个问题的原因和建议的解决方法?非常感谢。
更新:下面是一个小程序,我建表现出同样的问题的完整代码:
import java.util.Timer;
import java.util.TimerTask;
public class WorkThread extends Thread
{
private Timer timerThread;
private WakeUpTask timerTask;
public WorkThread()
{
timerThread = new Timer();
timerTask = new WakeUpTask(this);
}
public void run()
{
timerThread.schedule(timerTask, 0, 20);
while (true)
{
long startTime = System.nanoTime();
for (int i = 0; i < 50; i++)
{
int a = 1 + 1;
goToSleep();
}
long timeTaken = (System.nanoTime() - startTime)/1000000;
System.out.println("Time taken this loop: " + timeTaken + " milliseconds");
}
}
synchronized public void goToSleep()
{
try
{
wait();
}
catch (InterruptedException e)
{
System.exit(0);
}
}
synchronized public void wakeUp()
{
notifyAll();
}
private class WakeUpTask extends TimerTask
{
private WorkThread w;
public WakeUpTask(WorkThread t)
{
w = t;
}
public void run()
{
w.wakeUp();
}
}
}
所有主类所做的就是创建和启动这些工作线程之一。在Windows 7上,此代码产生大约999ms到1000ms的时间,这非常好。然而,在Windows XP上运行相同的jar会产生大约1562ms到1566ms的时间,这是在我测试过的两台独立的XP机器上。他们都运行Java 6更新27
我觉得这个问题正在发生,因为定时器沉睡了20毫秒(相当小的值) - 如果我塞子所有单个第二的执行环插入等待观望() - notifyAll()循环,这会产生正确的结果 - 我敢肯定,看到我想要做什么的人(以50fps模拟世嘉主系统)会看到这不是一个解决方案 - 但它不会给出交互式响应时间,每50次跳过49次。正如我所说,Win7可以很好地处理这个问题。很抱歉,如果我的代码太大:-(
有趣的问题。您是否可以提供展示此行为的独立代码片段? – NPE
如果你喜欢,我可以给你定时器的源代码吗?我会给你很多东西,但是Z80内核变得相当庞大;-) – PhilPotter1987
@aix *“独立代码片段”*自包含代码可能很短,但它不能(根据定义)是[片段](http://en.wikipedia.org/wiki/Snippet_%28programming%29)。我建议发布[SSCCE](http://pscode.org/sscce.html)。 –