删除 ThreadPoolExecutor 的所有排队任务

Posted

技术标签:

【中文标题】删除 ThreadPoolExecutor 的所有排队任务【英文标题】:Removing all queued tasks of an ThreadPoolExecutor 【发布时间】:2010-12-13 08:51:01 【问题描述】:

我对@9​​87654321@ 有一个相当简单的问题。我有以下情况:我必须使用队列中的对象,为它们创建适当的工作任务并将它们提交给 ThreadPoolExecutor。这很简单。但在关闭场景中,许多工作人员可能会排队等待执行。由于其中一个任务可能会运行一个小时,并且我希望相对快速地正常关闭应用程序,因此我想丢弃 ThreadPoolExecutor 中的所有排队任务,而已经处理的任务应该正常完成。

ThreadPoolExecutor 文档有一个remove() 方法,但只允许删除特定任务。 purge() 仅适用于已取消的未来任务。我的想法是清除包含所有排队任务的队列。 ThreadPoolExecutor 提供对此内部队列的访问,但文档指出:

方法 getQueue() 允许访问 用于监控目的的工作队列 和调试。这种方法的使用 出于任何其他目的强烈 气馁。

因此,抓住这个队列并清除它不是一种选择。此外,这个文档的 sn-p 说:

两种提供的方法, 删除(java.lang.Runnable)和清除() 可用于协助存储 大量回收时 排队的任务被取消。

怎么样?当然,我可以维护我提交给执行程序的所有任务的列表,并且在关闭的情况下,我遍历所有条目并使用 remove() 方法将它们从 ThreadPoolExecutor 中删除......但是......来吧,这是一个浪费内存并且维护这个列表很麻烦。 (例如删除已经执行的任务)

感谢任何提示或解决方案!

【问题讨论】:

【参考方案1】:

由于ExecutorService.shutdown() 做得不够,而ExecutorService.shutdownNow() 做得太多,我猜你必须在中间写一些东西:记住所有提交的任务,并在调用shutdown() 之后(或之前)手动删除它们。

【讨论】:

"启动有序关闭,执行之前提交的任务,但不会接受新任务。"我提交了几个不应再执行的任务。但是正在执行的任务不应该结束。 您编辑了答案,因此我的评论不再完全有效。然而,shutdownNow() 文档指出:“尝试停止所有正在执行的任务。”这不是我想要的。 :) 哦,对了,我看了一半的句子。好吧,我想你唯一的选择是记住所有提交的Runnables 并手动删除它们。我会相应地进行编辑。【参考方案2】:

Bombe 的答案正是您想要的。 shutdownNow() 使用 nuke and pave 方法停止一切。这是您能做的最好的事情,没有子类化您正在使用的 ThreadPoolExecutor 的实现。

【讨论】:

【参考方案3】:

一个可能有效的疯狂和不干净的解决方案(没有经过深思熟虑或测试)将覆盖您的 WorkerTasks 的 interrupt(),只有在设置了某些全局值的情况下才会在调用 interrupt() 时拒绝关闭它们通过 shutdownNow()。

那应该允许您使用shutdownNow() 否?

【讨论】:

【参考方案4】:

告诉你的线程池关闭,getQueue,将每个结果放入单独的 Runnables,使用 remove 方法删除每个 Runnable。根据队列的类型,您可能能够根据返回值提前停止删除。

基本上,这是抓取队列并清除它,仅通过有效的方法进行清除。您无需手动记住所有提交,而是使用线程池已经必须记住所有提交的事实。但是,您可能需要制作队列的防御性副本,因为我认为它是实时视图,因此如果您在实时视图上迭代/for-eaching,则删除可能会导致并发修改异常。

【讨论】:

【参考方案5】:

我曾经在一个具有长时间运行线程的应用程序上工作。我们在关机时执行此操作,

BlockingQueue<Runnable> queue = threadPool.getQueue();
List<Runnable> list = new ArrayList<Runnable>();
int tasks = queue.drainTo(list);

列表被保存到一个文件中。启动时,列表会重新添加到池中,因此我们不会丢失任何工作。

【讨论】:

很好地补充了我的问题。 :-) 我的情况下,任务的持久性不是问题。但是您的添加可能会对其他人有所帮助,谢谢! :) 我不确定这是正确的方法。如果您通过shutdown() 关闭,它会一直等到所有内容都终止(甚至是排队的任务),如果您使用shutdownNow() 执行此操作,则该方法已经返回给您排空的排队任务列表。仅供参考。【参考方案6】:

您是否考虑过包装 ExecutorService?创建一个

CleanShutdownExecutorService implements Executor 

将所有调用委托给另一个 Executor,但将 Futures 保存在自己的列表中。然后 CleanShutdownExecutorService 可以有一个 cancelRemainingTasks() 方法,该方法调用 shutdown(),然后在其列表中的所有 Futures 上调用 cancel(false)。

【讨论】:

这可能是最干净的方法,因此我接受这个答案。 :-) 即使我希望已经有类似的东西......所以:让我们编写这个东西。 :-)【参考方案7】:

awaitTermination(long timeout, TimeUnit unit) 关机后不工作吗?

executor.shutdown(); executor.awaitTermination(60, TimeUnit.SECONDS)

【讨论】:

【参考方案8】:

你可以试试allowCoreThreadTimeOut(true);

【讨论】:

您应该制定自己的答案并明确表明您实际上是在回答 OP。【参考方案9】:

这是一个老问题,但如果这对其他人有帮助:您可以在调用 shutdown() 时设置一个 volatile 布尔值,如果在真正开始之前设置了该布尔值,则每个提交的任务都会终止。这将允许真正开始完成的任务,但会阻止排队的任务开始其实际活动。

【讨论】:

【参考方案10】:

您可以创建自己的任务队列并将其传递给ThreadPoolExecutor 构造函数:

int poolSize = 1; // number of threads
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>();
Executor executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, queue);

当您清除代码中某处的队列时,其余任务将不会执行:

queue.clear();

【讨论】:

以上是关于删除 ThreadPoolExecutor 的所有排队任务的主要内容,如果未能解决你的问题,请参考以下文章

关于ThreadPoolExecutor线程池,该怎么处理

如何在不关闭 Executor 的情况下等待 ThreadPoolExecutor 中的所有任务完成?

ThreadPoolExecutor源码初探

ThreadPoolExecutor源码初探

Android 中使用 ThreadPoolExecutor

Android 中使用 ThreadPoolExecutor