Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java "scheduleAtFixedRate" alternative solution?

I have a Java application that is used to communicate with an embedded device over a UART connection (RS422). The host queries the microcontroller for data in 5 millisecond intervals. Up until recently I've been using ScheduledExecutorService scheduleAtFixedRate to call my communication protocol method, but it turns out scheduleAtFixedRate is very unreliable for this desired level of precision (as many other posts reveal). Among the data returned from the microcontroller is a timestamp (in microseconds), allowing me to verify the interval between received data packets independently of the JVM. Needless to say, the interval when using scheduleAtFixedRate varied wildly - up to 30 milliseconds between packets. Additionally, the scheduler will then try to overcompensate for the missed cycles by calling the Runnable several times within one millisecond (again, no surprise to anyone here).

After some searching, there seemed to be a consensus that the JVM simply could not be trusted to ensure any kind of precise scheduling. However, I decided to do some experimenting on my own and came up with this:

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);

The result of this was fantastic. Adjacent timestamps never varied by more than 0.1 milliseconds from the expected 5 millisecond interval. Despite this, something about this technique doesn't seem right, but I haven't been able to come up with anything else that works. My question is basically whether or not this approach is OK, and if not, what should I do instead?

(I am running Windows 10 with JDK 8_74)

like image 306
Ben Avatar asked Feb 12 '16 23:02

Ben


1 Answers

Based on the information I've received in the comments, I've decided to use leave my code essentially intact (with the exception of Thread.yield() which I've added to the while loop). I have used this for a few months now and am very satisfied with the performance from this approach. See the final code below.

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);
like image 134
Ben Avatar answered Sep 21 '22 12:09

Ben