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?