I am scheduling a task as:
ScheduledExecutorService dataService = Executors.newScheduledThreadPool(1);
Future<?> dataTimerHandle = dataService.scheduleAtFixedRate(runnable, 100, freq, TimeUnit.MILLISECONDS);
This works fine without a flaw.
However, when a certain flag becomes true
on user action, the task is no more required periodically, and needs to be executed just once. I then attempt cancelling the task and submitting it just once as follows:
if(!dynamicUpdate) {
dataTimerHandle.cancel(true);
dataTimerHandle = dataService.submit(runnable);
}
else { //Reschedule again:
dataTimerHandle = dataService.scheduleAtFixedRate(runnable, 100, freq, TimeUnit.MILLISECONDS);
}
But seems like the runnable is still executing periodically and cancel()
is not working as expected.
Is there an alternate strategy for this?
The problem is probably not in the Future's cancel()
method. Here is a small runnable example that appears to be doing exactly what you want:
import java.util.concurrent.*;
public class CancelPeriodicTask {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduler = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
scheduler.setRemoveOnCancelPolicy(true);
try {
new CancelPeriodicTask().test(scheduler);
} catch (Exception e) {
e.printStackTrace();
} finally {
int openTasks = scheduler.shutdownNow().size();
println("Finished, open tasks: " + openTasks);
// openTasks will be 1 when RemoveOnCancelPolicy is false
// and the executor is closed within the scheduled task-period.
}
}
private static long sleepTime = 25L;
public void test(ScheduledThreadPoolExecutor scheduler) throws Exception {
// sleep 5 times at scheduled interval
SleepTask sleepTask;
ScheduledFuture<?> scheduledSleep = scheduler.scheduleAtFixedRate(sleepTask = new SleepTask(), 0, 2 * sleepTime, TimeUnit.MILLISECONDS);
sleepTask.sleepTimes.await();
println("Cancelling scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
scheduledSleep.cancel(true);
Thread.sleep(2 * sleepTime);
println("Running sleepTask once.");
scheduler.submit(sleepTask);
Thread.sleep(2 * sleepTime);
scheduledSleep = scheduler.scheduleAtFixedRate(sleepTask, 0, 2 * sleepTime, TimeUnit.MILLISECONDS);
println("Re-scheduled scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
Thread.sleep(5 * sleepTime);
println("Cancelling scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
scheduledSleep.cancel(true);
}
class SleepTask implements Runnable {
public final CountDownLatch sleepTimes = new CountDownLatch(5);
public int sleepCount;
@Override public void run() {
println("Sleeping " + (++sleepCount));
try { Thread.sleep(sleepTime); } catch (Exception e) {
e.printStackTrace();
}
sleepTimes.countDown();
}
}
private static final long START_TIME = System.currentTimeMillis();
private static void println(String msg) {
System.out.println((System.currentTimeMillis() - START_TIME) + "\t " + msg);
}
}
This is expected since you are sending the cancel command to the wrong handle. When you call service.submit()
it returns a handle for the newly created future, and you can't use the very same handle to send cancel messages to future's created via other calls
Obviously you can shut down the executor service via sevice.shutdown()
to not start any runnable submitted after a certain moment
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