AndyF
AndyF

Reputation: 1094

ScheduledExecutorService and cancel behaviour

I'm trying to find a satisfactory answer for this:

What may cause ScheduledFuture#cancel(false) to return false? Essentially I'm creating a new scheduler like so:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> schedulerStatus = scheduler.scheduleWithFixedDelay(this, 0L, 1L, TimeUnit.SECONDS);

And within the run method I have something like the following:

public void run() {
    // Do some logic
    schedulerStatus.cancel(false);
}

I've looked at the JavaDoc but it doesn't really say anything helpful,

"or could not be cancelled for some other reason"

So any help in understanding what's exactly happening here would be great!

Thanks

UPDATE

To expand further upon my example above:

private final ScheduledExecutorService scheduler;
private volatile ScheduledFuture<?> schedulerStatus;

@Inject
public IndexChangeManagerImpl(IndexChangeMonitor monitor, IndexChangeObservable observable) {
    this.monitor = monitor;
    scheduler = Executors.newScheduledThreadPool(1);
}

@Override
public  void init() {
    if (schedulerStatus == null || schedulerStatus.isDone()) {
        // Kick of the scheduler to attempt to initiate the listener every second.
        schedulerStatus = scheduler.scheduleWithFixedDelay(this, 0L, 1L, TimeUnit.SECONDS);
    }
}

@Override
public void run() {
    try {
        // Before starting the monitor, ensure it is not currently running.
        monitor.shutdown();

        // Initiate the monitor.
        monitor.start();

        // Listener has been established, no more attempts necessary.
        schedulerStatus.cancel(false);

        lastLog = 0;
    } catch (ChangeMonitorException sfE) {
        long now = System.currentTimeMillis();

        if (lastLog == 0 || now - lastLog > 60000) {
            // Log every 60 seconds.
            DEBUG.error("Error attempting to initiate index change monitor.", sfE);
            lastLog = now;
        }
    }
}

Hopefully this code snippet demonstrates that the two reasons for cancel returning false are in fact impossible (noting this is all single threaded). As cancel is called within the single scheduled thread clearly the task is neither complete and can't have been cancelled already.

One thing to note is that cancel returning false is intermittent and occurs when the system is under load.

Any further thoughts?

Upvotes: 3

Views: 1465

Answers (1)

Brian
Brian

Reputation: 17329

It looks like it returns false if it's already done executing or it's already been cancelled.

From the source for the default Future implementation used by Java's default Executor implementations:

boolean innerCancel(boolean mayInterruptIfRunning) {
    for (;;) {
        int s = getState();
        if (ranOrCancelled(s))
            return false;
        if (compareAndSetState(s, CANCELLED))
            break;
    }
    if (mayInterruptIfRunning) {
        Thread r = runner;
        if (r != null)
            r.interrupt();
    }
    releaseShared(0);
    done();
    return true;
}

Upvotes: 3

Related Questions