ScheduledExecutorService 不执行具有 initialDelay 0 的任务
ScheduledExecutorService not executing task with an initialDelay 0
当使用 scheduleWithFixedDelay
或 scheduleAtFixedRate
时,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
但是当我使用 scheduleWithFixedDelay
或 scheduleAtFixedRate
而不是 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 的调用=] 延迟为零时不会很快安排。显然在 scheduleWithFixedDelay
和 scheduleAtFixedRate
调用中有一些开销,足以让程序在它们有机会进行第一次执行之前退出。
无论如何,您可以在我的代码版本中看到行为 running live at IdeOne.com。添加此行:
Thread.sleep( 1_000 * 5 ) ;
…在您关机之前为某些 scheduleWithFixedDelay
和 scheduleAtFixedRate
任务提供足够的时间来执行。
或者通过添加对 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。 scheduleWithFixedDelay
或 scheduleAtFixedRate
未执行任务
这是由于continueExistingPeriodicTasksAfterShutdown is false by default
/**
* False if should cancel/suppress periodic tasks on shutdown.
*/
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
我们可以在安排任务之前通过ScheduledThreadPoolExecutor#setExecuteExistingDelayedTasksAfterShutdownPolicy
and ScheduledThreadPoolExecutor#setContinueExistingPeriodicTasksAfterShutdownPolicy
覆盖这些参数。
为什么立即调用shutdown
,schedule
可以执行这个任务,但是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.
不是因为程序还没来得及第一次执行就退出了,而是因为线程池中的工作线程没有得到任务(被移除了,),然后线程池就停止了。这个时候,程序就退出了。如果不删除任务,程序不会立即退出。只有当线程池中的工作线程完成工作队列中的任务后,程序才会退出。 (因为线程池中的工作线程不是守护线程)。
scheduleWithFixedDelay
或 scheduleAtFixedRate
时,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
但是当我使用 scheduleWithFixedDelay
或 scheduleAtFixedRate
而不是 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 的调用=] 延迟为零时不会很快安排。显然在 scheduleWithFixedDelay
和 scheduleAtFixedRate
调用中有一些开销,足以让程序在它们有机会进行第一次执行之前退出。
无论如何,您可以在我的代码版本中看到行为 running live at IdeOne.com。添加此行:
Thread.sleep( 1_000 * 5 ) ;
…在您关机之前为某些 scheduleWithFixedDelay
和 scheduleAtFixedRate
任务提供足够的时间来执行。
或者通过添加对 awaitTermination
的调用来查看相同的效果,如
背后的原因可以在ScheduledThreadPoolExecutor
中找到,这是Executors.newSingleThreadScheduledExecutor()
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。 scheduleWithFixedDelay
或 scheduleAtFixedRate
未执行任务
这是由于continueExistingPeriodicTasksAfterShutdown is false by default
/**
* False if should cancel/suppress periodic tasks on shutdown.
*/
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
我们可以在安排任务之前通过ScheduledThreadPoolExecutor#setExecuteExistingDelayedTasksAfterShutdownPolicy
and ScheduledThreadPoolExecutor#setContinueExistingPeriodicTasksAfterShutdownPolicy
覆盖这些参数。
为什么立即调用shutdown
,schedule
可以执行这个任务,但是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.
不是因为程序还没来得及第一次执行就退出了,而是因为线程池中的工作线程没有得到任务(被移除了,),然后线程池就停止了。这个时候,程序就退出了。如果不删除任务,程序不会立即退出。只有当线程池中的工作线程完成工作队列中的任务后,程序才会退出。 (因为线程池中的工作线程不是守护线程)。