Java-kloknøjagtighed på Windows: 15-16ms

Indlæg af Hanne Mølgaard Plasc

Problem



Kloknøjagtigheden på Windows synes at have ændret sig i det seneste. Jeg kan huske, at det er meget nøjagtigt siden Windows 7 (1 ms opløsning), og nu hopper tiden i trin på 15 til 16 ms. Jeg bemærkede på grund af nogle (alligevel dårligt skrevet) enhedsforsøg fejler (tests kontrollerede, at der var nogen tid mellem skrivning og læsning af optegnelser).


Dette påvirker følgende implementeringer:



  • System.currentTimeMillis()

  • LocalTime.now()

  • LocalDateTime.now()

  • ZonedDateTime.now()



Jeg er godt klar over, at den forløbet tid skal måles ved hjælp af System.nanoTime() deltager, men jeg undrer mig alligevel, hvis jeg savnede noget, der ændrede klokopløsningen til 15-16ms.


Miljø:



  • Windows 10 version 1709

  • JRE/JDK 1.8.0\_171-b11 (64-bit)



Har nogen bemærket det også? Hvad kan årsagen til denne beslutningsændring være?


EDIT:

Testkode for at kontrollere denne adfærd (se output: tidsændringer og antal prøver før ændring)


@Test
public void testCurrentTimeMillis() {
    test(() -> System.currentTimeMillis());
}

@Test
public void testNanoTime() {
    test(() -> System.nanoTime());
}

@Test
public void testLocalTime() {
    test(() -> LocalTime.now());
}

@Test
public void testLocalDateTime() {
    test(() -> LocalDateTime.now());
}

@Test
public void testZonedDateTime() {
    test(() -> ZonedDateTime.now());
}

private <T> void test(Supplier<T> timeSupplier) {

    int samples = 20;
    String lastTimeString = null;
    int count = 0;
    while (samples > 0) {

        count++;
        String timeString = timeSupplier.get().toString();
        if (!timeString.equals(lastTimeString)) {
            System.out.println(timeString + " (" + count + ")");
            lastTimeString = timeString;
            count = 0;
            samples--;
        }
    }
}

Bedste reference


For at få lokal tid med den bedste nøjagtighed uanset platformen , kom jeg op på en løsning med er baseret på den aktuelle tid (måling, når den er forøget) som et anker og og offset baseret på System.nanoTime() deltas.


Fordele:



  • Nuværende tid med præcision ikke afhængig af platformen

  • høj nøjagtighed (nano tid)

  • bærbare (da java.time objekter faktisk kan bære nanosekunder præcision).



Kode (kan tilpasses til også at tjene LocalTime og LocalDateTime:


/**
 * Exact zoned date/time, compensates for the 15-16ms leaps 
 * of milliseconds time on some Java platforms.
 */
public final class ExactZonedDateTime {

    private static ZonedDateTime anchor;
    private static long anchorNanos;

    static {
        ZonedDateTime last = ZonedDateTime.now();
        while (((anchor = ZonedDateTime.now()).equals(last))) {
            anchorNanos = System.nanoTime();
        }
    }

    private ExactZonedDateTime() {
    }

    public static ZonedDateTime now() {
        return anchor.plusNanos(System.nanoTime() - anchorNanos);
    }
}


Efterfølgende opkald:



  2018-04-26T12: 51: 02.293079632 + 02: 00 [[Europa/Zürich]]
  2018-04-26T12: 51: 02.293524865 + 02: 00 [[Europa/Zürich]]
  2018-04-26T12: 51: 02.293598126 + 02: 00 [[Europa/Zürich]]
  2018-04-26T12: 51: 02.293660770 + 02: 00 [[Europa/Zürich]]
  2018-04-26T12: 51: 02.293725538 + 02: 00 [[Europa/Zürich]]