虚拟窗口服务器上的 Java 调度程序执行器计时问题
我们有一个Java应用程序,它需要在其他环境中运行虚拟(Hyper-V)Windows 2012 R2 Server。在此虚拟Windows服务器上执行时,它似乎会遇到奇怪的计时问题。我们将问题追溯到 Java 调度执行器中的不稳定调度:
public static class TimeRunnable implements Runnable {
private long lastRunAt;
@Override
public void run() {
long now = System.nanoTime();
System.out.println(TimeUnit.NANOSECONDS.toMillis(now - lastRunAt));
lastRunAt = now;
}
}
public static void main(String[] args) {
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
exec.scheduleAtFixedRate(new TimeRunnable(), 0, 10, TimeUnit.MILLISECONDS);
}
此代码应每 10 毫秒运行一次 TimeRunnable,它会在服务器上生成如下结果:
12
15
2
12
15
0
14
16
2
12
140
0
0
0
0
0
0
0
0
0
0
0
0
1
0
7
15
0
14
16
2
12
15
2
12
1
123
0
0
0
在其他计算机上,包括负载繁重的虚拟 Linux 机箱以及一些 Windows 桌面上,典型的运行如下所示:
9
9
10
9
10
9
10
10
9
10
9
9
10
10
9
9
9
9
10
10
9
9
10
10
9
9
10
9
10
10
10
11
8
9
10
9
10
9
10
10
9
9
9
10
9
9
10
10
10
9
10
我们在Windows Server和Hyper-V方面没有太多经验,所以任何人都可以为这种现象提供解释吗?这是一个Windows Server问题吗?Hyper-V?这些平台上的 Java 错误?有解决方案吗?
编辑:一位同事编写了同一程序的C#版本:
private static Stopwatch stopwatch = new Stopwatch();
public static void Main()
{
stopwatch.Start();
Timer timer = new Timer(callback, null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(10));
}
private static void callback(object state)
{
stopwatch.Stop();
TimeSpan span = stopwatch.Elapsed;
Console.WriteLine((int)span.TotalMilliseconds);
stopwatch.Restart();
}
下面是两个应用程序在虚拟 Windows 服务器上并排工作的更新(部分)屏幕截图:
编辑:Java程序的其他一些变体都产生(几乎)相同的输出:
- 替换为
System.nanoTime()
System.currentTimeMillis()
- 一种变体,其中被定期打印的StringBuilder所取代
System.out.println()
- 一种变体,其中调度机制被替换为单个线程,该线程通过
Thread.sleep()
- 其中 的变体是可变的
lastRunAt