等待系统时间继续申请

问题描述:

我已经写了类以继续开始JAVA应用,如果当前第二个是5(即Calender.SECOND%5 == 0)的倍数等待系统时间继续申请

的类代码是如下所示,我很好奇的是,我是否以正确的方式做这件事?它看起来不是一个优雅的解决方案,阻止像这样的执行并反复得到实例。

public class Synchronizer{ 
    private static Calendar c; 

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

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

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

更新:

我觉得我没有明确说明,但是我正在寻找在这里与系统时间同步没有做忙等待。

所以我需要能够得到

12:19:00 
12:19:05 
12:19:10 
... 
+0

您是否熟悉System.currentTimeMillis? – 2014-09-23 14:22:38

+0

您也可以使用[定时器](http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html),因为这是它的设计目的。 – Compass 2014-09-23 17:42:09

+0

我已经更新了一个更清晰问题的问题,但杜兰达的解释性答案对我来说非常满意。 – ickarsim 2014-09-24 09:26:25

现在你已经叫什么忙等待(有时也称为轮询),并且是其在处理器的使用方面效率低下,并在能源使用方面。您的代码在操作系统允许时执行,并且这样做可以防止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); 
    } 
} 

这工作得很好,如果你没有太严格的要求上精确打击的时候,你可以期望它回到合理地接近规定的时间(几十毫秒如果时间不是太远(几秒钟),那么可能就会离开。然而,你有没有保证,偶尔当操作系统真的很忙,它可能会很晚返回。

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

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

在多任务环境中(现在几乎所有地方),在高级编程语言中不可能等待准确的时间点。如果您有严格的要求,则需要转到特定操作系统以在指定时间创建中断,并在中断(即汇编程序或至少C代表中断处理程序)中处理事件。在大多数正常的应用中,你不需要这么做,在游戏/应用程序中,几个ms +/-通常无关紧要。

+0

正是我在找的是谢谢。 – ickarsim 2014-09-24 09:26:55

由于@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

了类似的问题,您应该使用的

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(); 
     }  
    } 
} 

第一个要点是,你绝对不能使用忙等待。在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正如其他答案中所建议的那样是错误的,因为只有在每第五秒的第一毫秒时才是正确的。