我有一个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);
结果很棒。相邻的时间戳从未与预期的5毫秒间隔相差超过0.1毫秒。尽管如此,关于这种技术的一些东西似乎并不合适,但我还没有能够提出任何有效的方法。我的问题基本上是这种方法是否正常,如果没有,我该怎么做呢?
(我使用JDK 8_74运行Windows 10)
答案 0 :(得分:2)
根据我在评论中收到的信息,我决定使用保留我的代码基本上完整(除了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);