CurrentTimeMillis() и ожидание, вращаясь на nanoTime()

Еще более важное обновление: я пробовал тот же код на Java 8, а System.currentTimeMillis() работает как ожидалось:

1449242048.588 368936.080131
1449242048.590 368936.082132
1449242048.592 368936.084132
1449242048.594 368936.086132
1449242048.596 368936.088133
1449242048.598 368936.090133
1449242048.600 368936.092133
1449242048.602 368936.094133
1449242048.604 368936.096133
1449242048.606 368936.098134
1449242048.608 368936.100134
1449242048.610 368936.102134
Main run took 20.036 seconds. Expected time: 20.0

Спасибо @Tunaki за подсказку.

Обновление: я думаю, что полезно указать ссылку указанную в ответе на дублированный вопрос:

System.currentTimeMillis() реализуется с использованием GetSystemTimeAsFileTime, который, по сути, просто читает низкую которое поддерживает Windows. Чтение этого глобальная переменная, естественно, очень быстрая - около 6 циклов в соответствии с сообщила информация. Это значение времени суток обновляется с постоянной независимо от того, как запрограммировано прерывание таймера - в зависимости от платформы это будет либо 10 мс, либо 15мс (это значение кажется привязанным к периоду прерывания по умолчанию).

(акцент мой)

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

Я заметил, что смешивание System.currentTimeMillis() с таким видом воспроизведения дает очень странные результаты, по крайней мере, в Windows 7. Время nano обновляется, как ожидалось, но миллисекундовое время перескакивает 16 миллисекунд за раз.

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

1449238154.610 365042.452692
1449238154.626 365042.454692
1449238154.626 365042.456692
1449238154.626 365042.458692
1449238154.626 365042.460692
1449238154.626 365042.462693
1449238154.626 365042.464693
1449238154.626 365042.466693
1449238154.626 365042.468693
1449238154.642 365042.470693
1449238154.642 365042.472694
1449238154.642 365042.474694
1449238154.642 365042.476694
1449238154.642 365042.478694
1449238154.642 365042.480694
Main run took 20.0 seconds. Expected time: 20.0

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

Может кто-нибудь объяснить, что происходит?

Я использую код, который я написал для тестирования этого эффекта. Среда - это Windows 7 Enterprise, Java 7, 8 ядер на двух процессорах.

public class NanotimeTest {

    private static final long DELAY_NANOS = 2_000_000;
    private static final int ITERATIONS = 10_000;

    private static class Times {
        public long millis;
        public long nanos;
    }

    Times[] times = new Times[ITERATIONS];

    public static void main(String[] args) {
        new NanotimeTest().run();
    }

    private void run() {
        for (int i = 0; i < ITERATIONS; i++) {
            times[i] = new Times();
        }


        System.out.println("Starting warmup");
        produceTimes(1000);

        System.out.println("Starting main run");
        long start = System.currentTimeMillis();
        produceTimes(DELAY_NANOS);
        long end = System.currentTimeMillis();


        for(Times t : times) {
            System.out.printf("%.3f %f\n", t.millis / 1000.0, t.nanos / 1_000_000_000.0);
        }

        System.out.println("Main run took " + (end - start)/1000.0 + " seconds. Expected time: " + (DELAY_NANOS * ITERATIONS)/1_000_000_000.0); 
    }

    private void produceTimes(long delayNanos) {
        for (int i = 0; i < ITERATIONS; i++) {

            long start;

            times[i].millis = System.currentTimeMillis();
            times[i].nanos = start = System.nanoTime();

            while (System.nanoTime() - start < delayNanos) {
                // go on
            }
        }
    }

}

Ответы