等待系统时间继续申请

时间:2014-09-23 14:18:35

标签: java datetime calendar blocking busy-waiting

如果当前秒数是5的倍数(即Calender.SECOND%5 == 0),我写了一个类来继续启动JAVA应用程序

下面列出了类代码,我很好奇的是,我这样做是正确的吗?它似乎不是一个优雅的解决方案,阻止这样的执行并反复获取实例。

public class Synchronizer{
    private static Calendar c;

    public static void timeInSync(){
        do{
            c = Calendar.getInstance();
        }
        while(c.get(Calendar.SECOND) % 5 != 0);
    }
}

在另一个类的构造函数中调用Synchronizer.timeInSync(),并在main方法的开头创建该类的实例。然后应用程序将永远运行一个每隔5秒调用一次的TimerTask。

是否有更清洁的解决方案来同步时间?

更新

我想我没有明确说明,但我在这里寻找的是与系统时间同步,而不是忙着等待。

所以我需要能够得到

12:19:00
12:19:05
12:19:10
...

4 个答案:

答案 0 :(得分:2)

您现在所拥有的是忙等待(有时也称为轮询),是的,它在处理器使用方面以及能源使用方面效率低下。只要操作系统允许,您就可以执行代码,这样做可以防止将CPU用于其他工作,或者当没有其他工作时,它会阻止CPU小睡,浪费能量(加热CPU,耗尽电池) ...)。

你应该做的是让你的线程睡觉,直到你想要做某事的时间到来。这允许CPU执行其他任务或进入休眠状态。

java.lang.Thread上有一个方法可以做到这一点:Thread.sleep(long milliseconds)(它还有一个堂兄带有额外的nanos参数,但是VM可以忽略纳米,并且这种精度很少需要。)

首先,您确定何时需要做一些工作。然后你睡到那时。一个天真的实现可能看起来像这样:

public static void waitUntil(long timestamp) {
    long millis = timestamp - System.currentTimeMillis();
    // return immediately if time is already in the past
    if (millis <= 0)
        return;
    try {
        Thread.sleep(millis);
    } catch (InterruptedException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

如果您没有对精确命中时间有太严格的要求,这可以正常工作,如果时间不是太远,您可以预期它会合理地返回到指定时间(可能是几十毫秒之外)未来(几秒钟)。但是,当操作系统真的很忙时,你偶尔会有无保证它可能会在很晚之后返回。

稍微准确一点的方法是确定所需的睡眠时间,一半的睡眠时间,再次评估所需的睡眠,再次睡眠一半时间等等,直到所需的睡眠时间变得非常小,然后忙等待剩下的几毫秒。

然而,System.currentTimeMillis()并不保证实际的时间分辨率;它可能每毫秒改变一次,但它也可能每10毫秒改变一次(这取决于平台)。 System.nanoTime()也一样。

在多任务环境(现在几乎无处不在)的高级编程语言中,无法等待确切的时间点。如果您有严格的要求,则需要转到操作系统细节以在指定时间创建中断并处理中断中的事件(这意味着汇编程序或至少C表示中断处理程序) 。在大多数普通应用程序中,您不需要这样做,几毫秒+/-通常在游戏/应用程序中无关紧要。

答案 1 :(得分:1)

正如@ChrisK建议的那样,只需直接拨打System.currentTimeMillis()即可简化。

例如:

    long time = 0;
    do
    {
        time = System.currentTimeMillis();

    } while (time % 5000 != 0);

请注意,您需要将比较值更改为5000,因为时间的表示以毫秒为单位。

此外,像这样直接进行任何比较都有可能存在缺陷,因为循环调用取决于处理器的可用性等等,因此有可能像这样的实现会使一个调用返回:

`1411482384999`

然后循环中的下一个调用返回

`1411482385001`

意味着由于硬件可用性而跳过了您的状况。

如果你想使用内置的调度程序,我建议在这里查看类似问题的答案java: run a function after a specific number of seconds

答案 2 :(得分:0)

你应该使用

System.nanoTime() 

而不是

System.currentTimeMillis()

因为它返回测量的经过时间而不是系统时间,所以nanoTime不受系统时间变化的影响。

public class Synchronizer
{
    public static void timeInSync()
    {
        long lastNanoTime = System.nanoTime();
        long nowTime = System.nanoTime();
        while(nowTime/1000000 - lastNanoTime /1000000 < 5000 )
        {
            nowTime = System.nanoTime();
        }    
    }
}

答案 3 :(得分:0)

第一个要点是你绝不能使用忙碌等待。在java中,您可以使用Object.wait(timeout)Thread.sleep(timeout)来避免繁忙等待。后者更适合您的情况,因为您的情况不需要丢失显示器锁定。

接下来,您可以使用两种方法等待,直到满足您的时间条件。您可以预先计算整个等待时间或等待循环中的小时间间隔,检查条件。

我将在这里说明这两种方法:

        private static long nextWakeTime(long time) {
            if (time / 1000 % 5 == 0) { // current time is multiple of five seconds
                return time;
            }
            return (time / 1000 / 5 + 1) * 5000;
        }


        private static void waitUsingCalculatedTime() {
            long currentTime = System.currentTimeMillis();
            long wakeTime = nextWakeTime(currentTime);

            while (currentTime < wakeTime) {
                try {
                    System.out.printf("Current time: %d%n", currentTime);
                    System.out.printf("Wake time: %d%n", wakeTime);
                    System.out.printf("Waiting: %d ms%n", wakeTime - currentTime);
                    Thread.sleep(wakeTime - currentTime);
                } catch (InterruptedException e) {
                    // ignore
                }
                currentTime = System.currentTimeMillis();
            }
        }

        private static void waitUsingSmallTime() {
            while (System.currentTimeMillis() / 1000 % 5 != 0) {
                try {
                    System.out.printf("Current time: %d%n", System.currentTimeMillis());
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        }

正如您所看到的,等待预先计算的时间更复杂,但它更精确,更有效(因为通常情况下,它将在单次迭代中完成)。迭代等待较小的时间间隔更简单,但效率和精确度较低(精度取决于所选的时间间隔大小)。

另请注意我如何计算时间条件是否满足:

 (time / 1000 % 5 == 0)

在第一步中,您需要计算秒数,然后检查它是否是五的倍数。按照其他答案中的建议按 time % 5000 == 0 进行检查是错误的,因为它仅适用于每五秒钟的第一毫秒。