ScheduledFuture returns null when a task is submitted or scheduleAtFixedRate

435 Views Asked by At

I am trying to schedule a job to run every 10 minutes using ScheduledThreadPoolExecutor. There are 10 threads in the thread pool. Code looks like:

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10, r -> {
                    Thread t = new Thread(r);
                    t.setName("thread");
                    return t;
                }, (runnable, pool) -> {
                });

List<ScheduledFuture<?>> listOfFutures = new ArrayList<>();

ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
    try {
        System.out.println("John is a good guy");
    } catch (Exception e) {
        // eat the exception
    }
}, 0, 10, TimeUnit.MINUTES);

listOfFutures.add(future);

Now, if 100 tasks were scheduled and their future were added to a list. When we try to iterate the list and do future.cancel(), out of 100, 2-3 futures turn out to be null.

What is the possible reason here?

I had to add an initial delay of 2 seconds in order to ensure that the future is not null. But I also have an ExecutorService with the same problem (future is null in few cases). But there is no delay that can be added there.

1

There are 1 best solutions below

2
DuncG On

Most likely it is that the executor pool has been shutdown or encounted some issue. You've not implemented the RejectedExecutionHandler very clearly, it just discards errors so you have no idea when they occur.

You can get more information on when / how any executor service issue occurs if you drop the last parameter to new ScheduledThreadPoolExecutor to remove the pointless handler (runnable, pool) -> {}. This will instead use the default abort policy.

Alternatively change your rejected handler so that it tells you of the problem when there is one:

(runnable, pool) -> { 
    throw new RejectedExecutionException("*** FAILED ***"
               +" isShutdown()="+pool.isShutdown()
               +" getActiveCount()="+pool.getActiveCount()
               +" queue size="+pool.getQueue().size());
}

When running with your original handler (runnable, pool) -> {} you will need to check each call as you otherwise will have no indication of when there is an issue:

ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> { ... }, 0, 10, TimeUnit.MINUTES);
if (future == null)
   throw new RuntimeException("*** FAILED to add new action ***");