ScheduledExecutorService 不执行具有 initialDelay 0 的任务

ScheduledExecutorService not executing task with an initialDelay 0

当使用 scheduleWithFixedDelayscheduleAtFixedRate 时,

ScheduledExecutorService 未执行具有 0 个时间单位的 initialDelay 的已提交任务。但是当我调用 schedule 时它正在执行任务

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

输出

I am a runnable

但是当我使用 scheduleWithFixedDelayscheduleAtFixedRate 而不是 schedule 时,任务没有被执行。

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();

为什么在这种情况下没有执行任务?我预计此任务将在 initialDelay 设置为 0

时执行

您过早关闭了执行器。这就是其他线程尚未启动的原因。不能保证线程会先启动然后关闭方法。

如果您使用 awaitTermination,您将看到输出。

scheduledExecutorService.awaitTermination(1, TimeUnit.MILLISECONDS);

//输出:我是可运行的

您对 ExecutorService#shutdown 的调用将停止任何进一步的任务安排。显然,您的调用发生得如此之快,以至于调度程序从未执行过计划任务的第一个 运行。这是有道理的。

不太明白的是为什么对 schedule 的调用恰好安排得足够快以使其任务在关机前及时执行,但对 scheduleWithFixedDelay 或 [=14 的调用=] 延迟为零时不会很快安排。显然在 scheduleWithFixedDelayscheduleAtFixedRate 调用中有一些开销,足以让程序在它们有机会进行第一次执行之前退出。

无论如何,您可以在我的代码版本中看到行为 running live at IdeOne.com。添加此行:

Thread.sleep( 1_000 * 5 ) ;

…在您关机之前为某些 scheduleWithFixedDelayscheduleAtFixedRate 任务提供足够的时间来执行。

或者通过添加对 awaitTermination 的调用来查看相同的效果,如 所示。

背后的原因可以在ScheduledThreadPoolExecutor中找到,这是Executors.newSingleThreadScheduledExecutor()

的实现class

1。调用 schedule,

时执行任务

这是由于executeExistingDelayedTasksAfterShutdown is true by default

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

2。 scheduleWithFixedDelayscheduleAtFixedRate

未执行任务

这是由于continueExistingPeriodicTasksAfterShutdown is false by default

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

我们可以在安排任务之前通过ScheduledThreadPoolExecutor#setExecuteExistingDelayedTasksAfterShutdownPolicy and ScheduledThreadPoolExecutor#setContinueExistingPeriodicTasksAfterShutdownPolicy覆盖这些参数。

为什么立即调用shutdownschedule可以执行这个任务,但是scheduleAtFixedRate不执行这个任务?

如果使用scheduleAtFixedRate,则此任务为Periodic,默认keepPeriodic策略为false,因此当shutdown 被调用。

但是使用schedule并没有执行删除任务的逻辑。(isPeriodic是错误的。)

参见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();
    }

所以我认为 Basil Bourque 的回答的解释是不准确的:

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.

不是因为程序还没来得及第一次执行就退出了,而是因为线程池中的工作线程没有得到任务(被移除了,),然后线程池就停止了。这个时候,程序就退出了。如果不删除任务,程序不会立即退出。只有当线程池中的工作线程完成工作队列中的任务后,程序才会退出。 (因为线程池中的工作线程不是守护线程)。