Java "scheduleAtFixedRate"替代解决方案?

标签 java scheduledexecutorservice

我有一个 Java 应用程序,用于通过 UART 连接 (RS422) 与嵌入式设备通信。主机以 5 毫秒为间隔向微 Controller 查询数据。直到最近我一直在使用 ScheduledExecutorService scheduleAtFixedRate 来调用我的通信协议(protocol)方法,但事实证明 scheduleAtFixedRate 对于这种所需的精度级别非常不可靠(正如许多其他帖子所揭示的那样)。从微 Controller 返回的数据中有一个时间戳(以微秒为单位),使我能够独立于 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);

结果非常棒。相邻时间戳与预期的 5 毫秒间隔的差异从不超过 0.1 毫秒。尽管如此,这种技术似乎有些不对劲,但我还没有想出任何其他有效的方法。我的问题基本上是这种方法是否可行,如果不行,我应该怎么做?

(我正在运行带有 JDK 8_74 的 Windows 10)

最佳答案

根据我在评论中收到的信息,我决定保留我的代码基本不变(我添加到 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);

关于Java "scheduleAtFixedRate"替代解决方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35373705/

相关文章:

java - 如何使用 ScheduledExecutorService 实现固定速率轮询器?

java - ScheduledExecutorService 未正确触发

java.util.concurrent.ScheduledExecutorService 运行频率很低

java - 读取单元格 Java POI null 错误

java - 字符数组的默认值

java - 如何在二维数组中每列的正下方打印列总计

java - 如何使用 ScheduledExecutorService 每天在特定时间运行特定任务?

java - 使用Spring Boot Profile和Gradle管理依赖项

java - 检查文件内容中缺少哪个参数

mysql - 在不使用参数计划的情况下在 mysql 数据库中输入新数据时通知 Logstash