ScheduledExecutorService not executing task with an initialDelay 0

Viewed 292

ScheduledExecutorService not executing a submitted task with an initialDelay of 0 time units when using scheduleWithFixedDelay or scheduleAtFixedRate. But it is executing the task when i call schedule on it

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = () -> System.out.println("I am a runnable");
scheduledExecutorService.schedule(runnable, 0, TimeUnit.SECONDS);
scheduledExecutorService.shutdown();

Output

I am a runnable

But when I use scheduleWithFixedDelay or scheduleAtFixedRate instead of schedule the task is not getting executed.

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = () -> System.out.println("I am a runnable");
scheduledExecutorService.scheduleWithFixedDelay(runnable, 0, 1, TimeUnit.SECONDS);
// or  scheduledExecutorService.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);
scheduledExecutorService.shutdown();

Why is the task not getting executed in this scenario? I expected this task to be executed as the initialDelay is set to 0

4 Answers

The reason behind can be found in ScheduledThreadPoolExecutor, which is the implementation class of Executors.newSingleThreadScheduledExecutor()

1. Task is executed when calling schedule,

it is due to executeExistingDelayedTasksAfterShutdown is true by default

    /**
     * False if should cancel non-periodic not-yet-expired tasks on shutdown.
     */
    private volatile boolean executeExistingDelayedTasksAfterShutdown = true;

2. Task is not executed for scheduleWithFixedDelay or scheduleAtFixedRate

it is due to continueExistingPeriodicTasksAfterShutdown is false by default

    /**
     * False if should cancel/suppress periodic tasks on shutdown.
     */
    private volatile boolean continueExistingPeriodicTasksAfterShutdown;

We can override these parameters by ScheduledThreadPoolExecutor#setExecuteExistingDelayedTasksAfterShutdownPolicy and ScheduledThreadPoolExecutor#setContinueExistingPeriodicTasksAfterShutdownPolicy before scheduling the tasks.

Why shutdown is called immediately, schedule can perform this task, but scheduleAtFixedRate does not perform this task?

If scheduleAtFixedRate is used, then this task is Periodic, and default keepPeriodic policy is false, so this task will be removed from work queue when shutdown is called.

But using schedule does not perform the logic of removing task.(isPeriodic is false.)

See ScheduledThreadPoolExecutor#onShutdown:

        @Override void onShutdown() {
        BlockingQueue<Runnable> q = super.getQueue();
        boolean keepDelayed =
            getExecuteExistingDelayedTasksAfterShutdownPolicy();
        boolean keepPeriodic =
            getContinueExistingPeriodicTasksAfterShutdownPolicy();
        // Traverse snapshot to avoid iterator exceptions
        // TODO: implement and use efficient removeIf
        // super.getQueue().removeIf(...);
        for (Object e : q.toArray()) {
            if (e instanceof RunnableScheduledFuture) {
                RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>)e;
                if ((t.isPeriodic()
                     ? !keepPeriodic
                     : (!keepDelayed && t.getDelay(NANOSECONDS) > 0))
                    || t.isCancelled()) { // also remove if already cancelled
                    if (q.remove(t))
                        t.cancel(false);
                }
            }
        }
        tryTerminate();
    }

So I think the explanation from Basil Bourque's answer is inaccurate:

Apparently there is some overhead in the scheduleWithFixedDelay and scheduleAtFixedRate calls, enough overhead that the program exits before they get a chance to make their first executions.

It's not because program exits before they get a chance to make their first executions, but because the worker threads in the thread pool did not get the task(It was removed,), and then the thread pool stop. At this time, the program exits. If the task is not removed, the program will not exit immediately. Only when the worker thread in the thread pool have completed the tasks in the work queue, the program will exit. (Because the worker thread in the thread pool are not daemon threads).

You shutdown the executor too early. That's why other thread is not started yet. There is no guarantee that thread will start first then shutdown method.

If you use awaitTermination you will see the output.

scheduledExecutorService.awaitTermination(1, TimeUnit.MILLISECONDS);

//Output: I am a runnable

Your call to ExecutorService#shutdown stops any further scheduling of tasks. Apparently your call happens so quickly that the scheduler never got to execute the first run of the scheduled task. This makes sense.

What does not quite make sense is why a call to schedule happens to get scheduled fast enough to get its task executed in time before the shutdown, but calls to scheduleWithFixedDelay or scheduleAtFixedRate with a delay of zero do not get scheduled as quickly. Apparently there is some overhead in the scheduleWithFixedDelay and scheduleAtFixedRate calls, enough overhead that the program exits before they get a chance to make their first executions.

Anyways, you can see the behavior in my version of your code running live at IdeOne.com. Adding this line:

Thread.sleep( 1_000 * 5 ) ;

… before your shutdown gives enough time for some of the scheduleWithFixedDelay and scheduleAtFixedRate tasks to execute.

Or see the same effect by adding a call to awaitTermination as shown in correct Answer by Nguyen.

Related