Schäfchen zählen mit Java

Schlafen Schlafen mit Java ist so eine Sache. Die Methode, die einem dazu am häufigsten über den Weg läuft ist: Thread.sleep(long milliseconds) Das funktioniert im Prinzip erstmal recht gut. Man kann Millisekunden angeben, was für meinen Anwendungsfall auch ausreichen würde. Das Problem ist nur, dass Schlafen im einstelligen Millisekunden-Bereich unter Windows (mindestens unter Windows XP) nicht funktioniert. Schläft man dort zyklisch für 5 Millisekunden so kehrt der Aufruf beispielsweise zwei Mal sofort zurück und schläft danach deutlich länger als 5 ms. Thread.sleep() ist dort also für kurze Zeiten nicht brauchbar.

Aber seit Java 5 gibt es noch: Thread.sleep(long milliseconds, int nanos) Klingt nach deutlich mehr Präzision oder? Das funktioniert nicht nur im Millisekunden-Bereich sondern man kann auch noch Nanosekunden angeben. Ähm nein… Die Implementierung der Methode ruft das dringende Bedürfnis in mir hervor, meinen Kopf auf die Tischkante zu schlagen (via Hay In A Needlestack):

<code class="language-java">    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }</code>

Ganz klasse. Das hätte ich auch noch selbst hin bekommen… Es gibt jedoch eine Funktion, mit der das Schlafen mit ausreichender Präzision sowohl unter Linux als auch unter Windows gut funktioniert: LockSupport.parkNanos(long nanos) Dieser Aufruf nutzt eine andere native Implementierung, die über sun.misc.Unsafe eingebunden wird.

Zeitmessung Auch bei der Zeitmessung gibt es ähnliche Probleme. Wer denkt, dass Millisekunden-Genauigkeit schon ok ist und System.currentTimeMillis() verwendet muss feststellen, dass der Aufruf nicht zwingend eine Genauigkeit von einer Millisekunde hat:

[...]the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.

Unter Linux ja, aber nicht unter Windows. Wie praktisch, wenn man schön vom Betriebssystem abstrahiert ist und sich dann bei der Genauigkeit doch wieder Gedanken machen muss, worauf man gerade läuft…

Seit Java 5 gibt es noch: System.nanoTime() Auch wenn die Dokumentation hier wieder Einschränkungen macht

This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().

funktioniert der Ansatz in der Praxis sowohl unter Linux als auch unter Windows zuverlässig.

TL; DR Wenn man mit Java betriebssystemunabhängig Zeiten unter 10ms Schlafen will, bzw. diese messen möchte sollte man die anderen genannten Methoden meiden und lieber die folgenden benutzen:

<code class="language-java">LockSupport.parkNanos()
System.nanoTime()</code>

Quellen und weitere Informationen My kingdom for a good timer Bug ID: 6313903 Inside the Hotspot VM: Clocks, Timers and Scheduling Events - Part I - Windows