Java"scheduleAtFixedRate"替代解决方案?

Ben*_*Ben 10 java scheduledexecutorservice

我有一个Java应用程序,用于通过UART连接(RS422)与嵌入式设备通信.主机以5毫秒的间隔向微控制器查询数据.直到最近,我一直在使用ScheduledExecutorService scheduleAtFixedRate来调用我的通信协议方法,但事实证明scheduleAtFixedRate对于这个所需的精度水平非常不可靠(正如许多其他帖子所揭示的那样).从微控制器返回的数据是时间戳(以微秒为单位),允许我独立于JVM验证接收数据包之间的间隔.不用说,使用scheduleAtFixedRate时的间隔变化很大 - 数据包之间最多30毫秒.此外,调度程序将尝试通过在一毫秒内多次调用Runnable来过度补偿错过的周期(同样,这里的任何人都不会感到惊讶).

经过一番搜索,似乎已经达成共识,即JVM根本无法被信任以确保任何类型的精确调度.但是,我决定自己做一些实验并想出这个:

Runnable commTask = () -> {
    // volatile boolean controlled from the GUI
    while(deviceConnection) {
        // retrieve start time
        startTime = System.nanoTime();
        // time since commProtocol was last called
        timeDiff = startTime - previousTime;

        // if at least 5 milliseconds has passed
        if(timeDiff >= 5000000) {
            // handle communication
            commProtocol();
            // store the start time for comparison
            previousTime = startTime;
        }
    }
};

// commTask is started as follows
service = Executors.newSingleThreadScheduledExecutor();
service.schedule(commTask, 0, TimeUnit.MILLISECONDS);
Run Code Online (Sandbox Code Playgroud)

结果很棒.相邻的时间戳从未与预期的5毫秒间隔相差超过0.1毫秒.尽管如此,关于这种技术的一些东西似乎并不合适,但我还没有能够提出任何有效的方法.我的问题基本上是这种方法是否正常,如果没有,我该怎么做呢?

(我使用JDK 8_74运行Windows 10)

Ben*_*Ben 2

根据我在评论中收到的信息,我决定保留我的代码基本完整(除了我已添加到 while 循环中的 Thread.yield() )。我已经使用它几个月了,对这种方法的性能非常满意。请参阅下面的最终代码。

Runnable commTask = () -> {
    // volatile boolean controlled from the GUI
    while(deviceConnection) {
        // retrieve start time
        startTime = System.nanoTime();
        // time since commProtocol was last called
        timeDiff = startTime - previousTime;

        // if at least 5 milliseconds has passed
        if(timeDiff >= 5000000) {
            // handle communication
            commProtocol();
            // store the start time for comparison
            previousTime = startTime;
        }
        Thread.yield();
    }
};

// commTask is started as follows
service = Executors.newSingleThreadScheduledExecutor();
service.execute(commTask);
Run Code Online (Sandbox Code Playgroud)