Michael HönnigMichael Hönnig
Während meiner aktuellen Arbeit an einem Programm mit weichen Realtime-Bedingungen (soft real-time system) kam ich in die Verlegenheit, wissen zu müssen, wie viel Zeit zwischen dem Auftreten eines Interrupts in einem Linux-Kernel-Modul und seiner Verarbeitung im Java-Programm vergangen ist, weil das Ergebnis eines Zyklus, der über 200ms benötigte, verworfen werden müssen. Java bietet aber nur einen absoluten Zeitstempel in Millisekunden und nur einen bezugslosen Zeitstempel in Nanosekunden. Angenommen, der Millisekunden-Wechsel von System.currentTimeMillis(), also dessen Erhöhung um 1, geschieht wirklich auf einer Grenze (restliche 6 Stellen zu Nanosekunden also 000000), dann kann mit folgender Klasse ein absoluter Nanosekunden Zeitstempel ermittelt werden:
class NanoSecondsTimestampProvider {

    private long nanoSecondsOffset, nanoSecondsError;

    public NanoSecondsTimestampProvider() {
        long curMilliSecs0, curMilliSecs1, 
               curNanoSecs, startNanoSecs, endNanoSecs;
        do {    
            startNanoSecs = System.nanoTime();
            curMilliSecs0 = System.currentTimeMillis(); 
            curNanoSecs = System.nanoTime();
            curMilliSecs1 = System.currentTimeMillis(); 
            endNanoSecs = System.nanoTime();
        } while ( curMilliSecs0 == curMilliSecs1 );
         
        nanoSecondsOffset = 1000000L*curMilliSecs1 - curNanoSecs;
        nanoSecondsError = endNanoSecs - startNanoSecs;
    }   

    public long getNanoSecondsDeviation() {
        return nanoSecondsError;
    }   

    public long currentNanoSecondsTimestamp() {
        return System.nanoTime() + nanoSecondsOffset;
    }   
}

Auf meinem Arbeitssystem ergibt sich dabei eine Abweichung von 2-4 Mikrosekunden, d.h. die Genauigkeit der Timestamps wird gegenüber System.currentTimeMillis() um den Faktor 250-500 verbessert. Für den Produktivbetrieb sollte evtl. noch der Konstruktor der Klasse in der Laufzeit beschränkt werden (falls aufgrund der Systemumstände ein Millisekunden-Wechsel zu selten ist) und für die Genauigkeit der Kalibration könnte ein Zielwert angegeben werden. Es könnte dann also z.B. 1 Sekunde lang probiert werden, eine Genauigkeit von 1 Mikrosekunde zu erreichen. Natürlich steht und fällt die tatsächliche Vergleichbarkeit mit dem echten Zeitstempel, der im Linux Kernel mit getnstimeofday(struct timestamp*) ermittelt werden kann mit der o.g. Annahme über den Wechsel des Millisekunden-Timestamps.