Search code examples
javaexecutorservicescheduledexecutorservice

Stopping and removing a task from ScheduledExecutorService


I know that I can throw an exception to suppress further executions of task that has been scheduled for repeated execution inside a ScheduledExecutorService (see this question).

I also know that I can setRemoveOnCancelPolicy(true) to make sure cancelled tasks are removed from the queue.

My questions are: Is the task actually removed from the scheduler also when I throw an exception from within it? Does this happen by implicitly cancelling the future? If yes, does this mean that setRemoveOnCancelPolicy() also bears on this case?

Couldn't find anything in the Javadocs.


Solution

  • I was wondering the same, couldn't find anything in the docs, so I tried it out. Observation:

    • Throwing a RuntimeException marks the future as done, not cancelled.
    • The Runnable is removed from the scheduler's queue, regardless of setRemoveOnCancelPolicy().

    Try it out yourself:

    public class SchedulerTest {
        protected final Logger log = LoggerFactory.getLogger(this.getClass());
    
        @Test
        public void schedulerExecutionException() throws Exception {
            log.info("Test: schedulerExecutionException");
    
            ScheduledThreadPoolExecutor sched = new ScheduledThreadPoolExecutor(2);
            sched.setRemoveOnCancelPolicy(true);
    
            ScheduledFuture future1 = sched.scheduleAtFixedRate(new Runnable() {
                int counter = 0;
                @Override
                public void run() {
                    log.info("Runnable 1: "+ ++counter);
    
                    if (counter >= 2) {
                        log.info("Runnable 1: BOOOM");
                        throw new RuntimeException("boom");
                    }
    
                }
            }, 1, 1, TimeUnit.SECONDS);
    
            ScheduledFuture future2 = sched.scheduleAtFixedRate(new Runnable() {
                int counter = 0;
                @Override
                public void run() {
                    log.info("Runnable 2: "+ ++counter);
                }
            }, 1, 1, TimeUnit.SECONDS);
    
            long cutoff = new Date().getTime() + 6000;
    
            while (new Date().getTime() < cutoff) {
                log.info("Scheduler Queue size: "+ sched.getQueue().size());
                log.info("Future 1: is "+ (future1.isCancelled() ? "" : "not ") +"cancelled, is "+ (future1.isDone()? "" : "not ") +"done");
                log.info("Future 2: is "+ (future2.isCancelled() ? "" : "not ") +"cancelled, is "+ (future2.isDone()? "" : "not ") +"done");
                Thread.sleep(1000);
            }
            assertEquals(sched.getQueue().size(), 1);
    
            future2.cancel(true);
            log.info("Scheduler Queue size: "+ sched.getQueue().size());
            log.info("Future 1: is "+ (future1.isCancelled() ? "" : "not ") +"cancelled, is "+ (future1.isDone()? "" : "not ") +"done");
            log.info("Future 2: is "+ (future2.isCancelled() ? "" : "not ") +"cancelled, is "+ (future2.isDone()? "" : "not ") +"done");
    
            assertEquals(sched.getQueue().size(), 0);
    
            sched.shutdownNow();
        }
    }