I have a stop watch which runs for specified amount of time (duration
parameter) using inner single threaded scheduled executor:
public class StopWatch {
private final AtomicBoolean running = new AtomicBoolean();
private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private long lastTimeMillis;
private long durationMillis;
private long elapsedMillis;
private ScheduledFuture<?> future;
public void start(long duration, TimeUnit timeUnit) {
if (running.compareAndSet(false, true)) {
durationMillis = timeUnit.toMillis(duration);
lastTimeMillis = System.currentTimeMillis();
elapsedMillis = 0;
future = executor.schedule(this::tick, 0, TimeUnit.MILLISECONDS);
}
}
(...)
private void tick() {
long now = System.currentTimeMillis();
elapsedMillis += now - lastTimeMillis;
lastTimeMillis = now;
// do some stuff
if (elapsedMillis < durationMillis) {
future = executor.schedule(this::tick, 100, TimeUnit.MILLISECONDS);
return;
}
running.compareAndSet(true, false);
}
(...)
}
My question is: will I run into any visibility problems with this approach (i.e. after executing .start()
on StopWatch again once first cycle is finished)?
elapsedMillis
and lastTimeMillis
are updated in two threads and durationMillis
is updated in first thread and read in second one, but it's happening rather sequentially and scheduled task starts after first thread is done with updating fields. I'm still not certain if skipping volatility on those fields is safe though (probably not).