Как измерить время, прошедшее в Java?

Всего 5 ответов


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

Если вы измеряете прошедшее время и хотите, чтобы оно было правильным , вы должны использовать System.nanoTime() . Вы не можете использовать System.currentTimeMillis() , если вы не против того, что ваш результат ошибочен.

Назначение nanoTime - измерение прошедшего времени, а назначение currentTimeMillis - измерение времени на настенных часах . Вы не можете использовать один для другой цели. Причина в том, что ни один компьютерный час не идеален; это всегда дрейфует и иногда должно быть исправлено. Это исправление может происходить либо вручную, либо в случае большинства машин существует процесс, который выполняется и постоянно выдает небольшие исправления системным часам («настенным часам»). Это часто случается. Другое такое исправление происходит всякий раз, когда есть високосная секунда.

Поскольку nanoTime является измерение истекшего времени, на него не nanoTime какие-либо из этих небольших поправок. Это то, что вы хотите использовать. Любые временные интервалы, которые в настоящее время происходят с currentTimeMillis будут отключены - возможно, даже отрицательными.

Вы можете сказать: «это не похоже на то, что это когда-нибудь будет иметь такое большое значение», на что я говорю, может быть, и нет, но в целом правильный код не лучше, чем неправильный код? Кроме того, nanoTime в любом случае короче печатать.

Ранее опубликованные заявления об отказе от nanoTime обычно имеющие точность только микросекунды, действительны. Кроме того, может потребоваться больше, чем целая микросекунда, чтобы вызвать, в зависимости от обстоятельств (как и другие), поэтому не ожидайте, что время очень очень маленькие интервалы правильно.


Какие типы использовать для достижения этого в Java?

Короткий ответ long . Теперь подробнее о том, как измерить ...

System.currentTimeMillis ()

«Традиционный» способ сделать это - использовать System.currentTimeMillis() :

long startTime = System.currentTimeMillis();
// ... do something ...
long estimatedTime = System.currentTimeMillis() - startTime;

oacltStopWatch

Обратите внимание, что Commons Lang имеет класс StopWatch, который можно использовать для измерения времени выполнения в миллисекундах. У него есть методы, такие как split() , suspend() , resume() и т. Д., Которые позволяют измерять результаты в разных точках выполнения и могут оказаться удобными. Посмотри на это.

System.nanoTime ()

Вы можете использовать System.nanoTime() если вы ищете чрезвычайно точные измерения прошедшего времени. Из своего Javadoc:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

Хамон

Другой вариант - использовать JAMon , инструмент, который собирает статистику (время выполнения, количество попаданий, среднее время выполнения, минимальное, максимальное и т. Д.) Для любого кода, который находится между методами start () и stop (). Ниже приведен очень простой пример:

import com.jamonapi.*;
...
Monitor mon=MonitorFactory.start("myFirstMonitor");
...Code Being Timed...
mon.stop();

Проверьте эту статью на www.javaperformancetunning.com для хорошего представления.

Использование AOP

Наконец, если вы не хотите загромождать свой код этими измерениями (или если вы не можете изменить существующий код), то АОП будет идеальным оружием. Я не собираюсь обсуждать это очень глубоко, но я хотел, по крайней мере, упомянуть об этом.

Ниже приведен очень простой аспект, использующий AspectJ и JAMon (здесь короткое имя pointcut будет использоваться для монитора JAMon, следовательно, вызов thisJoinPoint.toShortString() ):

public aspect MonitorAspect {
    pointcut monitor() : execution(* *.ClassToMonitor.methodToMonitor(..));

    Object arround() : monitor() {
        Monitor monitor = MonitorFactory.start(thisJoinPoint.toShortString());
        Object returnedObject = proceed();
        monitor.stop();
        return returnedObject;
    }
}

Определение pointcut может быть легко адаптировано для мониторинга любого метода на основе имени класса, имени пакета, имени метода или любой их комбинации. Измерение действительно идеальный вариант использования для АОП.


Ваш новый класс:

public class TimeWatch {    
    long starts;

    public static TimeWatch start() {
        return new TimeWatch();
    }

    private TimeWatch() {
        reset();
    }

    public TimeWatch reset() {
        starts = System.currentTimeMillis();
        return this;
    }

    public long time() {
        long ends = System.currentTimeMillis();
        return ends - starts;
    }

    public long time(TimeUnit unit) {
        return unit.convert(time(), TimeUnit.MILLISECONDS);
    }
}

Использование:

    TimeWatch watch = TimeWatch.start();
    // do something
    long passedTimeInMs = watch.time();
    long passedTimeInSeconds = watch.time(TimeUnit.SECONDS);

После этого прошедшее время можно преобразовать в любой формат, например, с помощью календаря.

Привет, GHad


Если цель состоит в том, чтобы просто выводить грубую информацию о синхронизации в журналы вашей программы, то простое решение для Java-проектов - не писать свои собственные классы секундомера или таймера, а просто использовать класс org.apache.commons.lang.time.StopWatch который является частью Apache Commons Lang.

final StopWatch stopwatch = new StopWatch();
stopwatch.start();
LOGGER.debug("Starting long calculations: {}", stopwatch);
...
LOGGER.debug("Time after key part of calcuation: {}", stopwatch);
...
LOGGER.debug("Finished calculating {}", stopwatch);

ТЛ; др

например, если начальное время это 23:00, а конечное время 1:00, чтобы получить продолжительность 2:00.

Невозможно. Если у вас есть только время суток, часы останавливаются в полночь. Без учета дат, как мы узнаем, имеете ли вы в виду 1:00 на следующий день, на следующей неделе или в следующем десятилетии?

Таким образом, переход с 23:00 до 01:00 означает движение назад во времени на 22 часа, движение стрелок часов против часовой стрелки. Смотрите результат ниже, минус двадцать два часа.

Duration.between(              // Represent a span of time a total number of seconds plus a fractional second in nanoseconds.
    LocalTime.of( 23 , 0 ) ,   // A time-of-day without a date and without a time zone. 
    LocalTime.of( 1 , 0 )      // A time-of-day clock stops at midnight. So getting to 1 AM from 11 PM means going backwards 22 hours.
)                              // Return a `Duration` object.
.toString()                    // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS

PT-22H

Пересечение полуночи требует большего контекста даты в дополнение к времени суток (см. Ниже).

Как измерить время, прошедшее в Java?

  1. Захватите текущий момент в UTC с помощью Instant.now() .
  2. Запечатлейте еще один такой момент позже.
  3. Передайте оба в Duration.between .
  4. (a) Из полученного объекта Duration извлеките количество 24-часовых дней, часов, минут, секунд и доли секунды в наносекундах, вызвав различные методы to…Part .
    (b) Или вызовите toString для генерации String в стандартном формате ISO 8601 PnYnMnDTnHnMnS .

Пример кода с использованием пары объектов Instant .

Duration.between(    // Represent a span of time a total number of seconds plus a fractional second in nanoseconds.
    then ,           // Some other `Instant` object, captured earlier with `Instant.now()`.
    Instant.now()    // Capture the current moment in UTC with a resolution as fine as nanoseconds, depending on the limits of your host computer hardware clock and operating system. Generally you will get current moment in microseconds (six decimal digits of fractional second) in Java 9, 10, and 11, but only milliseconds in Java 8. 
)                    // Return a `Duration` object.
.toString()          // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS

PT3M27.602197S

Новые технологии в Java 8+

У нас есть новая технология для этого, встроенная в Java 8 и более поздние версии, инфраструктура java.time .

java.time

Инфраструктура java.time определена JSR 310 , вдохновленной весьма успешным проектом Joda-Time , расширенным проектом ThreeTen-Extra и описанным в учебном руководстве по Oracle .

Старые классы даты и времени, такие как java.util.Date/.Calendar в комплекте с самыми ранними версиями Java, оказались плохо спроектированными, запутанными и хлопотными. Они вытесняются классами java.time.

разрешение

Другие ответы обсуждают решение.

Классы java.time имеют наносекундное разрешение, до девяти цифр десятичной доли секунды. Например, 2016-03-12T04:29:39.123456789Z .

И старые классы java.util.Date/.Calendar и классы Joda-Time имеют разрешение в миллисекундах (3 цифры дроби). Например, 2016-03-12T04:29:39.123Z .

В Java 8 текущий момент выбирается с разрешением до миллисекунды из-за устаревшей проблемы. В Java 9 и более поздних версиях текущее время может быть определено с точностью до наносекунды, если аппаратные часы вашего компьютера работают так хорошо.

Time-of-Day

Если вы действительно хотите работать только с временем суток, в котором отсутствует какая-либо дата или часовой пояс, используйте класс LocalTime .

LocalTime sooner = LocalTime.of ( 17, 00 );
LocalTime later = LocalTime.of ( 19, 00 );

Duration представляет собой промежуток времени, выраженный в секундах плюс наносекунды.

Duration duration = Duration.between ( sooner, later );

Дамп на консоль.

System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration );

раньше: 17:00 | позже: 19:00 | продолжительность: PT2H

ISO 8601

Обратите внимание, что вывод по умолчанию Duration::toString в стандартном формате ISO 8601 . В этом формате P отмечает начало (как в 'Period'), а T отделяет любую часть лет-месяцев-дней от части часов-минут-секунд.

Пересекая полночь

К сожалению, работа со временем суток становится сложной только тогда, когда вы пересекаете часы, пересекая полночь. Класс LocalTime обрабатывает это, предполагая, что вы хотите вернуться назад к более ранней точке дня.

Использование того же кода, что и выше, но переход с 23:00 на 01:00 приводит к получению отрицательных двадцати двух часов ( PT-22H ).

LocalTime sooner = LocalTime.of ( 23, 0 );
LocalTime later = LocalTime.of ( 1, 0 );

раньше: 23:00 | позже: 01:00 | продолжительность: PT-22H

Дата-время

Если вы намереваетесь пересечь полночь, возможно, имеет смысл работать со значениями даты-времени, а не только со временем дня.

Часовой пояс

Часовой пояс имеет решающее значение для дат. Таким образом, мы указываем три элемента: (1) желаемую дату, (2) желаемое время суток и (3) часовой пояс как контекст, с помощью которого можно интерпретировать эту дату и время. Здесь мы произвольно выбираем часовой пояс зоны Монреаля .

Если вы определяете дату только по смещению от UTC , используйте ZoneOffset с OffsetDateTime . Если у вас есть полный часовой пояс (смещение плюс правила для обработки аномалий, таких как переход на летнее время), используйте ZoneId с ZonedDateTime .

LocalDate localDate = LocalDate.of ( 2016, 1, 23 );
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime sooner = ZonedDateTime.of ( localDate, LocalTime.of ( 23, 0 ), zoneId );

Мы указываем более позднее время на следующий день в 1:00 утра.

ZonedDateTime later = ZonedDateTime.of ( localDate.plusDays ( 1 ), LocalTime.of ( 1, 0 ), zoneId );

Мы рассчитываем Duration таким же образом, как показано выше. Теперь мы получаем два часа, ожидаемые по этому Вопросу.

Duration duration = Duration.between ( sooner, later );

Дамп на консоль.

System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration );

раньше: 2016-01-23T23: 00-05: 00 [Америка / Монреаль] | позже: 2016-01-24T01: 00-05: 00 [Америка / Монреаль] | продолжительность: PT2H

Летнее время

Если бы время под рукой было связано с переходом на летнее время (DST) или другой подобной аномалией, классы java.time будут корректироваться по мере необходимости. Прочитайте документооборот класса для деталей.


О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии . Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date , Calendar и SimpleDateFormat .

Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии . Нет необходимости в строках, нет необходимости в java.sql.* .

Где взять классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API с комплексной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). Смотрите .

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval , YearWeek , YearQuarter и другие .


Есть идеи?

10000