使用持续时间为 1 毫秒且循环计数不确定的时间轴是否可以为秒表提供准确的时间?

Will using a Timeline with a duration of 1 millisecond with an indefinite cycle count provide an accurate time for a stopwatch?

我使用时间轴创建了一个简单的秒表,但我想知道它是否准确。这是代码:

LongProperty timeMillis = new SimpleLongProperty(0);
timer = new Timeline(new KeyFrame(Duration.millis(1), e -> timeMillis.set(timeMillis.get()+1));
timer.setCycleCount(Timeline.INDEFINITE);
timer.start();

我的问题是,在增加 timeMillis 的同时,每毫秒之间是否会有某种延迟?还是一次计数1毫秒导致的某种延迟?

根据 Jon Skeet 的建议,我自己计时,似乎还算准。

LongProperty timelineMillis = new SimpleLongProperty(0);
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(1), e -> timelineMillis.set(timelineMillis.get()+1)));
timeline.setCycleCount(Timeline.INDEFINITE);

Long startNano = System.nanoTime();
timeline.play();

Timeline checkTime = new Timeline(new KeyFrame(Duration.millis(1000), e -> {
    Long currentTimelineTime = timelineMillis.get();
    Long currentNanoTime = (System.nanoTime() - startNano) / 1000000;
    System.out.println("Timeline time: " + currentTimelineTime + ", Nano time: " + currentNanoTime + ", Difference: " + (currentTimelineTime - currentNanoTime));
}));
checkTime.setCycleCount(Timeline.INDEFINITE);
checkTime.play();

前 30 秒的输出:

Timeline time: 1010, Nano time: 1012, Difference: -2
Timeline time: 2002, Nano time: 2004, Difference: -2
Timeline time: 3010, Nano time: 3012, Difference: -2
Timeline time: 4002, Nano time: 4004, Difference: -2
Timeline time: 5010, Nano time: 5012, Difference: -2
Timeline time: 6002, Nano time: 6004, Difference: -2
Timeline time: 7010, Nano time: 7012, Difference: -2
Timeline time: 8002, Nano time: 8004, Difference: -2
Timeline time: 9010, Nano time: 9012, Difference: -2
Timeline time: 10002, Nano time: 10004, Difference: -2
Timeline time: 11010, Nano time: 11012, Difference: -2
Timeline time: 12002, Nano time: 12004, Difference: -2
Timeline time: 13010, Nano time: 13012, Difference: -2
Timeline time: 14003, Nano time: 14004, Difference: -1
Timeline time: 15011, Nano time: 15012, Difference: -1
Timeline time: 16003, Nano time: 16004, Difference: -1
Timeline time: 17011, Nano time: 17012, Difference: -1
Timeline time: 18002, Nano time: 18003, Difference: -1
Timeline time: 19010, Nano time: 19011, Difference: -1
Timeline time: 20002, Nano time: 20004, Difference: -2
Timeline time: 21010, Nano time: 21012, Difference: -2
Timeline time: 22002, Nano time: 22004, Difference: -2
Timeline time: 23010, Nano time: 23012, Difference: -2
Timeline time: 24002, Nano time: 24004, Difference: -2
Timeline time: 25010, Nano time: 25012, Difference: -2
Timeline time: 26002, Nano time: 26004, Difference: -2
Timeline time: 27010, Nano time: 27012, Difference: -2
Timeline time: 28002, Nano time: 28004, Difference: -2
Timeline time: 29010, Nano time: 29012, Difference: -2
Timeline time: 30002, Nano time: 30004, Difference: -2

您的时间轴方法相当准确,但计算时间的频率远高于 UI 能够呈现的时间。默认情况下,JavaFX 以每秒 60 帧的速度限制帧渲染,因此只有大约 6% 的 timemillis.set(...) 调用会在 UI.

中表示

一种更有效的方法是使用 AnimationTimer,它在呈现 UI 时执行计算,但不会更频繁地执行计算。 AnimationTimer 有一个 handle() 方法,保证每个渲染脉冲只执行一次:

LongProperty timeMillis = new SimpleLongProperty(0);

AnimationTimer stopwatch = new AnimationTimer() {

    private static final long STOPPED = -1 ;
    private long startTime = STOPPED ;

    @Override
    public void handle(long timestamp) {
        if (startTime == STOPPED) {
            startTime = timestamp ;
        }
        long elapsedNanos = timestamp - startTime ;
        long elapsedMillis = elapsedNanos / 1_000_000 ;
        timeMillis.set(elapsedMillis);
    }

    @Override
    public void stop() {
        startTime = STOPPED ;
        super.stop();
    }
};

现在只需调用 stopwatch.start()stopwatch.stop() 即可启动和停止计时器。您也可以添加一些 pause() 功能,这需要更多的工作...