2014-09-23 5 views
1

Я написал класс для продолжения запущенного приложения 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() вызывается в конструкторе другого класса и экземпляр этого класса создается в начале основного метода. Затем приложение запускается вечно с помощью TimerTask, который вызывается каждые 5 секунд.

Есть ли более чистые решения для синхронизации времени?

Update:

Я думаю, что я четко не указано, но то, что я ищу здесь синхронизации с системным временем, не делая занятости ожидания.

Так что мне нужно, чтобы быть в состоянии получить

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

Вы знакомы с System.currentTimeMillis? –

+0

В качестве альтернативы вы можете использовать [Таймер] (http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html), так как для этого он предназначен. – Compass

+0

Я уточнил вопрос для более четкого вопроса, но объяснительный ответ Дюрандаля очень удовлетворительный для меня. – ickarsim

ответ

2

То, что вы сейчас называется занят ждет (также иногда называют опроса), да и его неэффективное с точки зрения использования процессора, а также с точки зрения использования энергии.Вы выполняете код каждый раз, когда OS позволяет это, и при этом он предотвращает использование процессора для других работ или когда нет другой работы, он не позволяет 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); 
    } 
} 

Это прекрасно работает, если вы не слишком строгие требования к точности удара времени, вы можете ожидать, что она вернется достаточно близко к указанному времени (в нескольких десятках мса вероятно,), если время не слишком далеко в будущем (несколько секунд). Однако у вас есть нет гарантий, что иногда, когда ОС действительно занята, что она, возможно, возвращается намного позже.

Несколько более точный метод заключается в определении reuired времени сна, сон в течение половин то время, оценить необходимый сон снова, сон снова половину временем и так далее, пока требуемое время сна не становится очень мало, то ожидают оставшиеся миллисекунды.

Однако System.currentTimeMillis() не гарантирует фактическое разрешение времени; он может меняться один раз в миллисекунду, но он может меняться только каждые десять мс на 10 (это зависит от платформы). То же самое касается System.nanoTime().

Ожидание точного момента времени невозможно на языках программирования высокого уровня в многозадачной среде (практически везде в настоящее время). Если у вас есть строгие требования, вам нужно обратиться к спецификациям операционной системы, чтобы создать прерывание в указанное время и обработать событие в прерывании (что означает ассемблер или не менее C для обработчика прерываний). Вам не нужно, чтобы в большинстве обычных приложений несколько мс +/- обычно не имеют значения в игре/приложении.

+0

Именно то, что я искал, спасибо. – ickarsim

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

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(); 
     }  
    } 
} 
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, как предложено в другом ответе, неверна, так как это верно только для первой миллисекунды каждой пятой секунды.

 Смежные вопросы

  • Нет связанных вопросов^_^