There is a peculiarity that I encountered while using Java scheduled executors and was wondering if what I experienced is normal.
I need to schedule tasks that execute at a predefined rate of 5 seconds. It is expected that these tasks will take longer than 5 seconds to execute from time to time, but when the time to run them goes below 5 seconds, the backed up list of tasks should run in quick succession to catch up. When running the tasks, it is important to know what the original scheduled execution time was (think scheduledExecutionTime()
in java.util.TimerTask
). Finally, I need to track the difference between scheduled time and actual time to identify when the schedule is "drifting" and by how much.
So far I have implemented all of this by using Java executors, and the following class illustrates the general idea:
public class ExecutorTest {
public static final long PERIOD = 5000;
public static void main(String[] args) {
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
new Command(), 0, PERIOD, TimeUnit.MILLISECONDS);
}
private static final class Command implements Runnable {
long timestamp = 0;
public void run() {
long now = System.currentTimeMillis();
if (timestamp == 0) {
timestamp = now;
}
// Drift is the difference between scheduled time and execution time
long drift = now - timestamp;
String format = "Ran at %1$tF %<tT,%<tL; drift: %2$dms";
System.out.println(String.format(format, now, drift));
timestamp += PERIOD;
}
}
}
Running the code listed above shows that the drift (which ideally should be as close to 0 as possible) fluctuates by as much as a few seconds, the result of which is that tasks as executed either prematurely or late. I have created a graph from the results of running this for about 150 minutes:
So my first question is whether this is normal. My environment consists of 32 bit Windows XP and Java 1.5 update 21 (although Java 6 update 22 produces similar results).
The second question is whether there is a simple way to reduce the amount of drift. If I use a simple java.util.Timer
or even just Thread.sleep()
, the drift is non-existent.
Lastly, is there a better way of tracking the scheduled execution time when using scheduled executors?
The scheduled executor service uses System.nanoTime which doesn't drift as much as currentTimeMillis. Except if you are running on an XP system with more than one CPU socket. There is a bug in XP where the OS call System.nanoTime() uses is not consistent between sockets so as the thread switches which socket it is running on, you can expect to see this jumping around. (This is not a problem on Vista/7)
On a Linux system with one socket your program reports 0 - 3 ms drift.
Try this program.
public static void main(String... args) throws Exception {
long start = System.nanoTime();
long time = start;
while(time < start + 3e10) {
long now = System.nanoTime();
if (now < time || now > time + 50000) {
System.out.println(now - time);
now = System.nanoTime();
}
time = now;
}
}
On an i7 system I see approx 10 jumps of up to 2 ms. If I use the machine I see more. What I expect you might see is large negative and positive timings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With