有一个桌面应用程序,使用Thread.sleep()实现长时间(几分钟或几小时)的延迟。自Windows XP到(至少)Windows 7,这个应用程序一直运行良好。该应用程序计算需要多久才能做某事,然后执行Thread.sleep(msToWait)。即使系统在等待期间进入S3睡眠状态,这也可以正常工作。
然而,在Windows 10上,如果机器处于S3状态,则Thread.sleep()后的代码不会“准时”执行。似乎机器从“msToWait”开始执行代码,加上机器处于S3中的时间(现在不确定,但很可能是这样)。
早期版本的Windows没有表现出这种行为;Thread.sleep()后的代码始终等待正确的时间,无论睡眠状态如何。
测试使用的是当前的JVM 1.7。
这是Windows 10的bug吗?这是JVM的bug吗?有没有解决办法?
额外的数据:
开发了一个测试程序和过程。该过程是运行程序,使机器睡眠约一分钟,然后唤醒机器并等待程序完成。
如果在报告为8的Windows 10上运行具有JVM版本:25.40-b25的程序,则会失败:
C:\Users\Tester\Downloads>SleepTester.exe
Wed Apr 01 10:47:35 PDT 2015 Using default number of minutes: 5
Wed Apr 01 10:47:35 PDT 2015 You can use "SleepTester -minutes 10" to have it sleep for 10 minutes, for example.
Wed Apr 01 10:47:35 PDT 2015 JVM Version: 25.40-b25 Windows Version: Windows 8
Wed Apr 01 10:47:35 PDT 2015 The program will now wait for 5 minutes. Expect wrap-up at Wed Apr 01 10:52:35 PDT 2015
Wed Apr 01 10:53:38 PDT 2015 The system has come through the Thread.sleep(300000).
Wed Apr 01 10:53:38 PDT 2015 This should be a low number: 63589
Wed Apr 01 10:53:38 PDT 2015 This appears to be operating incorrectly...the expected sleep time has NOT been achieved.
Wed Apr 01 10:53:38 PDT 2015 Program is ending.
如果在Windows 7上运行该进程,则不会失败。
Wed Apr 01 17:12:18 EDT 2015 Java Runtime Version: 1.8.0_31-b13 JVM Version: 25.31-b07 Windows Version: Windows 7
Wed Apr 01 17:12:18 EDT 2015 The program will now wait for 6 minutes. Expect wrap-up at Wed Apr 01 17:18:18 EDT 2015
Wed Apr 01 17:18:18 EDT 2015 The system has come through the Thread.sleep(360000).
Wed Apr 01 17:18:18 EDT 2015 This should be a low number: 0
Wed Apr 01 17:18:18 EDT 2015 Program is ending.
这是测试程序:
import java.util.Date;
public class SleepTester {
private static int mMinutes;
private static int mDefault = 5;
public static void main(String[] args) throws Exception {
for (int iArg = 0; iArg < args.length; ++iArg) {
if (args[iArg].equals("-minutes") && (iArg + 1) < args.length) {
mMinutes = Integer.parseInt(args[++iArg]);
}
}
if (mMinutes == 0) {
mMinutes = mDefault;
System.out.println(new Date() + " Using default number of minutes: " + mDefault);
System.out.println(new Date() + " You can use \"SleepTester -minutes 10\" to have it sleep for 10 minutes, for example.");
}
System.out.println(new Date() + " Java Runtime Version: " + System.getProperty("java.runtime.version") + " JVM Version: " + System.getProperty("java.vm.version") + " Windows Version: " + System.getProperty("os.name"));
long msDelay = mMinutes * 60 * 1000;
long wakePoint = new Date().getTime() + msDelay;
System.out.println(new Date() + " The program will now wait for " + mMinutes + " minutes. Expect wrap-up at " + new Date(wakePoint));
Thread.sleep(msDelay); // If the machine goes into S3 during this interval, it should not matter, as long as it's awake when it fires.
System.out.println(new Date() + " The system has come through the Thread.sleep(" + msDelay + "). ");
long msAccuracy = Math.abs(new Date().getTime() - wakePoint);
System.out.println(new Date() + " This should be a low number: " + msAccuracy);
if (msAccuracy > 1000) System.out.println(new Date() + " This appears to be operating incorrectly...the expected sleep time has NOT been achieved.");
System.out.println(new Date() + " Program is ending.");
}
}
我意识到我可以尝试其他睡眠方法,但我认为既然我已经记录下来并发表在这里,在尝试其他方法之前,我会把它发布在这里。
其他信息:此错误似乎也出现在Windows 8中(但不是7或更早版本)。
附加4/4/2019
该问题可在bugs.java.com的以下网址[JDK-8221971]上看到。有一些早期的错误链接到该错误。来自链接JDK-8146730错误的评论:
17-04-2017 对这个问题有什么最新进展吗?
04-04-2019 已经被延迟。这是一个低优先级、复杂的问题,没有人积极处理它。
附加2/17/2021
这可能是由于Windows操作系统对超时响应方式的改变所致。即使直接使用Windows API,我也不确定如何实现使旧的和新的Windows操作系统表现相同的目标。
Windows XP、Windows Server 2003、Windows Vista、Windows 7、Windows Server 2008和Windows Server 2008 R2:dwMilliseconds值包括在低功耗状态下花费的时间。例如,计时器在计算机处于睡眠状态时仍在倒计时。
Windows 8、Windows Server 2012、Windows 8.1、Windows Server 2012 R2、Windows 10和Windows Server 2016:dwMilliseconds值不包括在低功耗状态下花费的时间。例如,计时器在计算机处于睡眠状态时不再倒计时。
https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex
TimeUnit.XXX.sleep(someQuantity)
或者Thread.nanoSleep()
(前者只是后者的包装器),看看是否有所改善?另外,使用的JVM是什么版本? - fgeExecutors.newScheduledThreadPool(1)
创建一个ScheduledExecutorService
,并使用schedule
提交一个任务,是否也存在相同的问题值得一看。 - Powerlord