Windows 10 上的 Java Thread.sleep() 在 S3 睡眠状态下停止
有一个桌面应用程序使用 Thread.sleep() 来实现长时间(几分钟或几小时)的延迟.从 Windows XP 到(至少)Windows 7,这个相同的应用程序一直运行良好.该应用程序计算它需要在未来多长时间内做某事,然后点击 Thread.sleep(msToWait).这一直运行良好,即使系统在等待期间碰巧进入 S3 睡眠状态.
不过,从 Windows 10 开始,Thread.sleep() 之后的代码不会按时"执行.如果机器已经在 S3 中.机器似乎在msToWait"开始执行代码.加上机器在 S3 中的时间(目前还不能 100% 确定,但很可能).
早期版本的 Windows 没有表现出这种行为;Thread.sleep() 之后的代码等待了适当的时间,与睡眠状态无关.
已在当前 JVM 1.7 上进行测试.
这是 Windows 10 的错误吗?这是一个 JVM 错误吗?有解决办法吗?
附加数据:
开发了一个测试程序和程序.过程是运行程序,让机器休眠大约一分钟,然后唤醒机器,等待程序完成.
如果此程序在 JVM 版本为 25.40-b25 的 Windows 10(报告为 8)上运行,则会失败:
C:UsersTesterDownloads>SleepTester.exe2015 年 4 月 1 日星期三 10:47:35 PDT 使用默认分钟数:5Wed Apr 01 10:47:35 PDT 2015 您可以使用SleepTester -minutes 10"例如,让它睡 10 分钟.2015 年 4 月 1 日星期三 10:47:35 PDT JVM 版本:25.40-b25 Windows 版本:Windows 82015 年 4 月 1 日星期三 10:47:35 PDT 程序现在将等待 5 分钟.预计在 2015 年 4 月 1 日星期三 10:52:35 PDT 进行总结Wed Apr 01 10:53:38 PDT 2015 系统已通过 Thread.sleep(300000).Wed Apr 01 10:53:38 PDT 2015 这应该是一个低数字:63589Wed Apr 01 10:53:38 PDT 2015 这似乎运行不正确...尚未达到预期的睡眠时间.Wed Apr 01 10:53:38 PDT 2015 计划即将结束.
如果进程在 Windows 7 上运行,它不会失败.
2015 年 4 月 1 日星期三 17:12:18 EDT Java 运行时版本:1.8.0_31-b13 JVM 版本:25.31-b07 Windows 版本:Windows 72015 年 4 月 1 日星期三 17:12:18 EDT 程序现在将等待 6 分钟.预计在 2015 年 4 月 1 日星期三 17:18:18 EDT 进行总结Wed Apr 01 17:18:18 EDT 2015 系统已通过 Thread.sleep(360000).Wed Apr 01 17:18:18 EDT 2015 这应该是一个低数字:0Wed Apr 01 17:18:18 EDT 2015 计划即将结束.
这是测试程序:
import java.util.Date;公共类 SleepTester {私有静态int mMinutes;私有静态int mDefault = 5;公共静态 void main(String[] args) 抛出异常 {for (int iArg = 0; iArg < args.length; ++iArg) {if (args[iArg].equals("-minutes") && (iArg + 1) < args.length) {mMinutes = Integer.parseInt(args[++iArg]);}}如果(mMinutes == 0){mMinutes = mDefault;System.out.println(new Date() + "使用默认分钟数:" + mDefault);System.out.println(new Date() + "您可以使用 "SleepTester -minutes 10" 让它休眠 10 分钟,例如.");}System.out.println(new Date() + " Java 运行时版本:" + System.getProperty("java.runtime.version") +" JVM 版本:" + System.getProperty("java.vm.version") + "Windows 版本:"+ System.getProperty("os.name"));长 msDelay = mMinutes * 60 * 1000;long wakePoint = new Date().getTime() + msDelay;System.out.println(new Date() + " 程序现在将等待 " + mMinutes + " 分钟.预计在 " + new Date(wakePoint)) 处结束.线程.sleep(msDelay);//如果机器在此时间间隔内进入 S3,则无关紧要,只要它在触发时处于唤醒状态即可.System.out.println(new Date() + " 系统已经通过Thread.sleep(" + msDelay + ").");long msAccuracy = Math.abs(new Date().getTime() - wakePoint);System.out.println(new Date() + "这应该是一个小数字:" + msAccuracy);if (msAccuracy > 1000) System.out.println(new Date() + "这似乎运行不正确...未达到预期的睡眠时间.");System.out.println(new Date() + "程序正在结束.");}}
我意识到我可以尝试各种其他方法来睡觉,但我认为既然我经历并记录了这一点,我会在尝试其他方法之前将其发布在这里.
附加信息:此故障似乎也出现在 Windows 8(但不是 7 或更早版本)中.
2019 年 4 月 4 日添加
该问题在 bugs.java.com 上的以下 URL [JDK-8221971][1] 上可见.有一些早期的错误与该错误相关联.来自链接 JDK-8146730 错误的评论:
<块引用>2017 年 17 月 4 日有关于这个话题的消息吗?
<块引用>
04-04-2019 它已被推迟.这是一个低优先级且复杂的问题,没有人主动分配给它.
2021 年 2 月 17 日补充
这可能是由于 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://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex
解决方案这是预期的有效行为.文档非常明确,声明:
<块引用>这些睡眠时间不能保证是精确的,因为它们受到底层操作系统提供的设施的限制.
和:
<块引用>在任何情况下,您都不能假设调用 sleep
会在指定的时间段内暂停线程.
There's a desktop application that uses Thread.sleep() to achieve long (minutes or hours) delays. This same application has been working fine from Windows XP through (at least) Windows 7. The application calculates how far in the future it needs to do something, then hits a Thread.sleep(msToWait). This has been working fine, even if the system happens to go into S3 sleep state during the wait.
As of Windows 10, though, the code after Thread.sleep() does not execute "on time" if the machine has been in S3. It appears that the machine begins executing code at "msToWait" plus the time the machine has been in S3 (not 100% sure of this right now, but likely).
Earlier versions of Windows did not exhibit this behavior; code after Thread.sleep() waited the right amount of time, irrespective of sleep status.
Testing has been on the current JVM 1.7.
Is this a Windows 10 bug? Is this a JVM bug? Is there a work-around?
ADDITIONAL DATA:
A test program and procedure were developed. The procedure is to run the program, cause the machine to sleep for about a minute, then wake the machine and wait for the program to finish.
If this the program is run on Windows 10 (reporting as 8) with JVM Version: 25.40-b25, it fails:
C:UsersTesterDownloads>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.
If the process is run on Windows 7, it does not fail.
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.
This is the test program:
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.");
}
}
I realize I could try various other methods to sleep, but I thought that since I went through and documented this, I'd post it here before trying other things.
Additional Information: This failure seems also to appear in Windows 8 (but not 7 or prior).
ADDITION 4/4/2019
The issue is visible on bugs.java.com at the following url [JDK-8221971][1].There are a few earlier bugs linked to that bug. A comment from the linke JDK-8146730 bug:
17-04-2017 Any news on this topic?
04-04-2019 It has been deferred. It is a low priority, and complex, issue no one is actively assigned to it.
ADDITION 2/17/2021
This is probably due to a change in the way the Windows operating system has been programmed to respond to timeouts. Even using Windows API directly, I'm unsure how to accomplish the goal of making old and new Windows OS's behave the same.
Windows XP, Windows Server 2003, Windows Vista, Windows 7, Windows Server 2008 and Windows Server 2008 R2: The dwMilliseconds value does include time spent in low-power states. For example, the timeout does keep counting down while the computer is asleep.
Windows 8, Windows Server 2012, Windows 8.1, Windows Server 2012 R2, Windows 10 and Windows Server 2016: The dwMilliseconds value does not include time spent in low-power states. For example, the timeout does not keep counting down while the computer is asleep.
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex
解决方案This is expected, valid behavior. The documentation is very explicit, stating:
these sleep times are not guaranteed to be precise, because they are limited by the facilities provided by the underlying OS.
and:
In any case, you cannot assume that invoking
sleep
will suspend the thread for precisely the time period specified.
相关文章