Reputation: 688
I need a replacement of java.util.Timer or java.util.concurrent.ScheduledExecutorService that accepts a time source so that it can be driven off of system time or some other reference. I use this timer to periodically execute a method, but need the period to be driven by system time when the application is in "live" mode and some other source when the application is in "playback" mode.
I did see that there are some 3rd party libraries such as guava stopwatch with functionality similar to what I am asking, but I prefer something native to Java due to the policies of the information system I am working on.
I may end up having to write my own, but am asking here since this seems like a need that many would have... Either when working with applications that have alternate concepts of time passage or for testing of units with embedded timers.
Upvotes: 0
Views: 378
Reputation: 688
In case anyone else needs it, here is the solution I ended up with.
public interface TimeSource {
long currentTimeMillis();
}
public class AlternateTimeSource implements TimeSource {
long currentTime = 0;
public AlternateTimeSource() {
}
public setTime(long time) {
if (time > currentTime) currentTime = time;
}
@Override
public long currentTimeMillis() {
return currentTime;
}
}
public class SystemTimeSource implements TimeSource {
@Override
public long currentTimeMillis() {
return System.currentTimeMillis();
}
}
class SourcedTimerThread extends Thread {
TimeSource reference_;
TimerTask task_;
long period_;
long executionTime_;
boolean done = false;
SourcedTimerThread(TimeSource reference,TimerTask task,long delay,long period) {
reference_ = reference;
task_ = task;
period_ = period >= 0 ? period : 0;
executionTime_ = reference_.currentTimeMillis() + (delay >= 0 ? delay : 0);
setName("SourcedTimerThread_"+reference_.getClass().getSimpleName());
start();
}
public void cancel() {
done = true;
interrupt();
}
public void run() {
long currentTime;
while (!done) {
try {
currentTime = reference_.currentTimeMillis();
if (executionTime_>currentTime) {
// Task hasn't yet fired; wait
sleep(1); }
else {
task_.run();
if (period_ > 0) {
// Repeating task, reschedule
executionTime_ = ((period_ < 0) ? currentTime-period_ : executionTime_ + period_);
}
else {
// Non-repeating task, done
break;
}
}
} catch (InterruptedException e) {
}
}
}
}
Usage:
// Build source
TimeSource source = null;
if (alternate) {
source = new AlternateTimeSource();
}
else {
source = new SystemTimeSource();
}
// Configure timer
SourcedTimerThread t = new SourcedTimerThread(source,new TimerTask() {
@Override
void run() {
System.out.println("Yay!");
}
}, delayMs, periodMs);
// Drive timer if necessary
if (alternate) {
// Drive timer based on alternate source such as data
source.setTime(dataRecord.getTime());
}
// When ready to cleanup
t.cancel();
Upvotes: 0
Reputation: 21114
You can implement the java.util.concurrent.ScheduledExecutorService
with a custom time source, by doing the following:
Compute the offset or the delta between the custom time source and system time (at initialization), and hold on to the delta in your implementation of ScheduledExecutorService
. When schedule
(and similar methods in the interface) are called, use the same offset that you computed at initialization and apply those offsets on the TimeUnits
being provided. That way - you can rely on the existing implementation of the scheduler.
Upvotes: 2