何时使用 ScheduledThreadPoolExecutor 的 "removeOnCancel" 策略

When to use ScheduledThreadPoolExecutor's "removeOnCancel" Policy

Java Java 的 ScheduledThreadPoolExecutor 的文档说明如下:

When a submitted task is cancelled before it is run, execution is suppressed. By default, such a cancelled task is not automatically removed from the work queue until its delay elapses. While this enables further inspection and monitoring, it may also cause unbounded retention of cancelled tasks. To avoid this, set setRemoveOnCancelPolicy(boolean) to true, which causes tasks to be immediately removed from the work queue at time of cancellation.

我将此解释为

问题

removeOnCancelPolicy 设置为 true 会产生任何后果吗?

我正在考虑这样做,以确保工作队列不会变得太大并占用太多内存,但我很困惑为什么默认情况下不启用此策略。

正在取消

通过调用方法 Future.cancel(boolean) 取消了任务。

因此,假设您通过 schedule 安排了一项任务,这意味着在执行开始之前队列会出现延迟:

Creates and executes a one-shot action that becomes enabled after the given delay.

Runnable task = new Callable<String>() {
    public String call() { return "Hello Future World!"; }
};

Future<String> future = executor.schedule(task, 50L, TimeUnit.SECONDS);
future.cancel(true);

任务被推送到这个执行器的工作队列,等待执行(至少50L, SECONDS的指定delay)。 在此期间,您可以取消它。但是根据 removeOnCancelPolicy 策略,它要么保留在队列中直到执行开始 (false),要么将立即删除 (true)。

清除(相关设计注意事项)

  • 如果我们 cancel 一个 ScheduledFutureTask 会怎样?

行为取决于 class ScheduledThreadPoolExecutor 的策略 removeOnCancel - 请参阅此 inner-class 方法的实现 ScheduledFutureTask.cancel

public boolean cancel(boolean mayInterruptIfRunning) {
    boolean cancelled = super.cancel(mayInterruptIfRunning);
    if (cancelled && removeOnCancel && heapIndex >= 0)
        remove(this);
    return cancelled;
}
  • 如果任务仅被取消但未被删除(根据政策)会怎样?

设计考虑了这一点。查看父 class ThreadPoolExecutor 的方法 remove(runnable) and especially purge():

Tries to remove from the work queue all Future tasks that have been cancelled. This method can be useful as a storage reclamation operation, that has no other impact on functionality. Cancelled tasks are never executed, but may accumulate in work queues until worker threads can actively remove them. Invoking this method instead tries to remove them now. However, this method may fail to remove tasks in the presence of interference by other threads.

队列是稀缺资源

A queue,尤其是阻塞队列在某种程度上是有限的(例如容量:容纳最大项目)为什么你应该总是努力保持他们的短,而不是太多的工作负载排队,尽快处理任务

因此文档警告 removeOnCancelPolicy set false:

may also cause unbounded retention of cancelled tasks

因此该策略对队列、其填充级别(最大容量)、可能拒绝进一步提交的任务等有影响

您可以通过检查执行程序的底层 BlockingQueue 来试验此策略的行为:它的大小,上面 Future 的元素。他们的isCancelled状态等

用例:查看已取消的任务及以后

如果队列 保留已取消的任务 那么这允许 put the user in control (Usability principle),re-/un-do 可以减少工作量,重新安排可能是一个有用的功能(如下所示示例说明)。

正在取消任务

假设您有一个带有一些 UI 的报告调度程序。当用户安排报告时,报告将(有一些延迟)提交到您的 executor/queue.

鉴于用户安排了一份报告,一秒钟后发现它得到了错误的参数。因为他们会导致报告到运行很长,所以他们立即取消了预定的报告。他们很幸运,还不算太晚。

现在:这意味着额外的工作,因为必须更正或重新输入错误的参数并重新安排报告。

正在重新安排任务

他们不想重新输入所有参数,而是想重新安排现有已取消的参数,只需修改几个参数,然后点击“提交”或“安排”。

没有忘记,原谅了!

如果策略 removeOnCancelPolicy 设置为 false

this enables further inspection and monitoring

一个视图列出了所有任务,按状态(已取消、已计划、运行正在、已完成等)过滤,以便可以选择已取消的任务并查看其详细信息(计划、参数)看过。此外,“重新安排”操作使再次提交变得容易。

@hc_dev 的回答很好,解释了将 removeOnCancelPolicy 设置为 true 的许多后果,但是将该值设置为 true 的另一个后果 - 阻塞队列的并发性在您的 ScheduledExecutorService 范围内可能会受到影响。

默认情况下,由 Executors.newScheduledThreadPool 工厂方法创建的 ScheduledExecutorService 使用 ScheduledThreadPoolExecutor.DelayedWorkQueue 作为其工作队列。这个工作队列由一个包含在其中的数组组成,由 一个锁保护。 这意味着当一个线程正在 removeing 来自 ScheduledThreadPoolExecutor.DelayedWorkQueue 的任务时,其他线程每次取消任务时,都必须等待才能从工作队列中推送或弹出任何内容。因此,设置 removeOnCancelPolicy 将提高对阻塞队列锁的争用并降低队列的并发性。

Javadoc 对 ScheduledThreadPoolExecutor.DelayedWorkQueue 的删除方法的性能有这样的说法:

A DelayedWorkQueue is based on a heap-based data structure like those in DelayQueue and PriorityQueue, except that every ScheduledFutureTask also records its index into the heap array. This eliminates the need to find a task upon cancellation, greatly speeding up removal (down from O(n) to O(log n)), and reducing garbage retention that would otherwise occur by waiting for the element to rise to top before clearing.

因此,remove 方法的性能非常好,并且可以很好地适应队列中的许多条目,但要记住这一点。