1

I've written a class to continue a started JAVA application if the current second is a multiple of 5 (i.e. Calender.SECOND % 5 == 0)

The class code is presented below, what I'm curious about is, am I doing this the right way? It doesn't seem like an elegant solution, blocking the execution like this and getting the instance over and over.

public class Synchronizer{
    private static Calendar c;

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

Synchronizer.timeInSync() is called in another class's constructor and an instance of that class is created at the start of the main method. Then the application runs forever with a TimerTask that's called every 5 seconds.

Is there a cleaner solution for synchronizing the time?

Update:

I think I did not clearly stated but what I'm looking for here is to synchronization with the system time without doing busy waiting.

So I need to be able to get

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

4 回答 4

4

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

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

java.lang.Thread 上有一个方法可以做到这一点:(Thread.sleep(long milliseconds)它还有一个表亲采用额外的 nanos 参数,但 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 毫秒更改 10 毫秒(这取决于平台)。System.nanoTime() 也是如此。

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

于 2014-09-23T17:40:09.530 回答
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

于 2014-09-23T14:31:40.640 回答
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();
        }    
    }
}
于 2014-09-23T14:57:07.470 回答
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是错误的,因为仅在每五秒的第一毫秒才是正确的。

于 2014-09-23T18:17:44.137 回答