【问题标题】:Java Thread.sleep() on Windows 10 stops in S3 sleep statusWindows 10 上的 Java Thread.sleep() 在 S3 睡眠状态下停止
【发布时间】:2015-06-06 07:15:40
【问题描述】:

有一个桌面应用程序使用 Thread.sleep() 来实现长时间(几分钟或几小时)的延迟。从 Windows XP 到(至少)Windows 7,这个相同的应用程序一直运行良好。该应用程序计算它需要在多远的将来做某事,然后点击 Thread.sleep(msToWait)。这一直运行良好,即使系统在等待期间碰巧进入 S3 睡眠状态。

不过,从 Windows 10 开始,如果机器处于 S3 中,则 Thread.sleep() 之后的代码不会“按时”执行。机器似乎在“msToWait”加上机器在 S3 中的时间开始执行代码(现在不是 100% 肯定,但很可能)。

早期版本的 Windows 没有表现出这种行为; Thread.sleep() 之后的代码等待了适当的时间,与睡眠状态无关。

已在当前 JVM 1.7 上进行测试。

这是 Windows 10 的错误吗?这是一个 JVM 错误吗?有解决办法吗?

附加数据:

开发了一个测试程序和程序。过程是运行程序,让机器休眠大约一分钟,然后唤醒机器,等待程序完成。

如果此程序在 JVM 版本为 25.40-b25 的 Windows 10(报告为 8)上运行,则会失败:

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 或更早版本)中。

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

【问题讨论】:

  • 不确定错误在哪里,但您可以尝试使用TimeUnit.XXX.sleep(someQuantity)Thread.nanoSleep()(前者只是后者的包装器),看看它是否有所作为?还有,什么JVM?
  • 我建议尝试不同的 JVM 版本(如果您使用的 JVM 版本...比 Windows 10 旧或新,也许会有所不同)。然后有一个非常可靠的方法来确定它是否是一个 JVM 错误:去写一个针对 Oracle 的缺陷,看看会发生什么;-)
  • 如果ScheduledExecutorService(通过Executors.newScheduledThreadPool(1); 创建,然后使用schedule 向其提交作业,也一定有同样的问题。
  • @Dale 使用 Java 8u40 测试它仍然是一个好主意。
  • 我不知道为什么它以前有效,但我认为行为方式是预期的。无论如何,这不是在 Windows 上执行此操作的好方法。您应该使用具有绝对时间的可等待计时器(相对时间的行为与睡眠完全相同)。它们自 Windows XP 以来就存在:docs.microsoft.com/en-us/windows/desktop/sync/…(如果机器已配置为睡眠时间和超时,它们甚至可以唤醒机器)。我可以提供一个 C++ 示例,但我不知道如何在 Java 中使用它们 :-)

标签: java windows windows-8 windows-10 sleep-mode


【解决方案1】:

这是预期的有效行为。 documentation 非常明确,声明:

这些睡眠时间不能保证是精确的,因为它们受到底层操作系统提供的设施的限制。

和:

在任何情况下,您都不能假设调用sleep 会在指定的时间段内暂停线程。

【讨论】:

  • 考虑到旧版本的 Windows 在 S3 期间没有“停止时间”,这不是预期的。我不是要求精确,这就是这两个片段中正在解决的问题。我要问的是为什么新版本的 Windows 的行为明显不同。
  • 引用的文档没有说明底层操作系统的挂起状态,这是这里问题的核心。
  • 规范说精度不能保证,因此您可能不期望精度。一个底层操作系统碰巧提供了精度这一事实并不意味着 Java 有义务在未来永远保持这种精度。 Java 从未承诺过精确。
  • 我们对精度有不同意见。显然,如果睡眠时间不准确小时(或更多),您认为这是一个精度问题。我没有。
  • @Dale “我要问的是为什么新版本的 Windows 的行为明显不同。” 这听起来更像是SuperUser 的问题,不是吗?
【解决方案2】:

这可能是 Windows 10 中的一个错误。即使是在 Metro 中完成的重要操作(如开始菜单)有时也无法及时从挂起状态唤醒(这并不奇怪,这取决于您设置了防火墙或禁用的所有内容)。我会建议使用 processhacker 或一些 sysinternals 工具检查线程/进程的状态,并尝试提出一些解决方案。然后检查 Windows 事件日志等。

或者只是做一些愚蠢的事情,比如用一个信号量替换睡眠,然后在命令行中执行睡眠。

文档非常确定它应该及时唤醒,只是你不应该依赖它作为某些数据线上的输出计时器,播放音乐等,但是花费 100 倍的时间有点不同.

【讨论】:

    猜你喜欢
    • 2011-05-12
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多