How to cancel pending items from a ScheduledThreadPoolExecutor?
Asked Answered
L

3

10

I have a task which requires me to schedule tasks and remove them when a certain event occurs. I'm using a ScheduledThreadPoolExecutor to schedule the tasks, that's pretty straightforward. But, I found two ways to cancel the pending items, both of them look a bit odd.

I'm curious if any of them is at production quality. If neither of them, then what do you suggest?

Here is a skeleton of what I do:

private final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);

public void doStuff() {
    //...
    scheduler.schedule(new Runnable() {/*...*/}, 10, TimeUnit.MILISECONDS)
}

public void actOnEvent() {
    cancelPendingItems();
    doMoreStuff();
}

public void cancelPendnigItems() {
    // TODO implement me
}

This is candidate 1:

public void cancelPendingItems() {
    scheduler.getQueue().clear();
}

This is candidate 2:

public void cancelPendingItems() {
    for (Runnable task : scheduler.getQueue()) {
        scheduler.remove(task);
    }
}

Both look like a hack to me because they depend on the ScheduledThreadPoolExecutor.queue property which isn't specified in the ScheduledExecutor interface. I'm a bit worried that I might violate an invariant of ScheduledThreadPoolExecutor and I will detect it too late.

So, are these snippets going to do what I want them to do? Are there any better/cleaner ways to do it?

Lodicule answered 24/9, 2012 at 16:38 Comment(0)
P
11

Note that ScheduledExecutorService.schedule returns a ScheduledFuture. Just store these and when your cancel method is called, use the cancel method on the stored futures to cancel their future iterations.

Pharmacology answered 24/9, 2012 at 16:42 Comment(1)
Where should these be stored for a web app? Is it possible to store them in a database? I schedule events days in advance and some are no longer relevant by the time they come around. Do I have to run them anyway? Or is there some way to persist the future in a database so that I can cancel the event?Spermine
C
5

I would use a List<Future>:

private final List<Future> futures = ...

public void doStuff() {
    futures.add(scheduler.schedule(new Runnable() {/*...*/}, 10,
        TimeUnit.MILISECONDS));
}

public void cancelPendingItems() {
    for(Future future: futures)
        future.cancel(true);
    futures.clear();
}
Cadel answered 24/9, 2012 at 16:42 Comment(1)
Thanks! Fortunately, it fits my actual problem because there aren't too many possible tasks so this cleanup will be reasonably quick. Which is important for our resource-constrained application.Lodicule
G
2

Future.cancel(boolean mayInterruptIfRunning)

Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

Or you can also use ScheduledThreadPoolExecutor#shutdownNow

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. This implementation cancels tasks via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

So you need to handle InterruptedException to allow safe exit of threads which are being canceled.

Gleiwitz answered 24/9, 2012 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.