清除ThreadPoolExecutor中所有排队的任务

31
我有一个关于 ThreadPoolExecutor 的简单问题。我的情况是:我需要从队列中消耗对象,为它们创建适当的工作任务并将它们提交到 ThreadPoolExecutor 中。这很简单。但在关闭场景中,可能会有许多工作任务排队等待执行。由于其中一个任务可能运行一小时,而我希望相对快速地正常关闭应用程序,因此我希望丢弃 ThreadPoolExecutor 中所有排队的任务,而已经处理的任务应该正常完成。
ThreadPoolExecutor 文档有一个 remove() 方法,但只允许删除特定任务。purge() 仅适用于已取消的 Future 任务。我的想法是清除持有所有排队任务的队列。ThreadPoolExecutor 提供了访问此内部队列的方法,但文档指出:
引用: 方法 getQueue() 允许访问工作队列以进行监视和调试。强烈不建议将此方法用于任何其他目的。
因此,获取此队列并清除它不是一个选项。此外,文档的这段代码说:
引用: 两个提供的方法 remove(java.lang.Runnable) 和 purge() 可用于协助存储回收,当大量排队任务被取消时。

如何实现呢?当然,我可以维护一个我提交给执行器的所有任务列表,在关闭情况下,我遍历所有条目并使用remove()方法从ThreadPoolExecutor中删除它们... 但是...拜托,这浪费内存,而且维护这个列表也很麻烦。(例如,删除已执行的任务)

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

10个回答

15

我曾在一款存在长时间运行线程的应用程序上工作。我们在关闭时进行此操作。

BlockingQueue<Runnable> queue = threadPool.getQueue();
List<Runnable> list = new ArrayList<Runnable>();
int tasks = queue.drainTo(list);
列表被保存到一个文件中。在启动时,列表会被添加回池中,以便我们不会丢失任何作业。

2
很好的补充到我的问题中。:-)对于我来说,任务的持久性不是问题。但你的补充可能会帮助其他人,谢谢! :) - Malax
我不确定那是否是正确的方法。如果你使用shutdown()来关闭,它会等待所有任务终止(包括排队的任务),但如果你使用shutdownNow(),该方法会立即返回已排队任务的列表。只是提供信息而已。 - Whimusical

11

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

CleanShutdownExecutorService implements Executor 

该执行器将所有调用委托给另一个执行器,但保留其自己的 Future 列表。CleanShutdownExecutorService 可以有一个 cancelRemainingTasks() 方法,该方法调用 shutdown(),然后在其列表中对所有 Future 调用 cancel(false)。


1
这可能是最清晰的方法,因此我接受这个答案。 :-)即使我希望已经有类似的东西……所以:让我们编写这个东西。 :-) - Malax

6

1
启动有序关机程序,在此过程中,先前提交的任务将被执行,但不会接受新的任务。我已经提交了几个任务,这些任务不应再执行。但是正在执行的任务不应该结束。 - Malax
你编辑了你的回答,所以我的评论不再完全有效。尽管如此,shutdownNow()文档说明:“尝试停止所有正在执行的任务。”这不是我想要的。 :) - Malax
哦,对了,我漏读了句子的一半。那么,我想你唯一的选择就是记住所有提交的“Runnable”,然后手动删除它们。我会相应地进行编辑。 - Bombe

5

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


3
您可以创建自己的任务队列并将其传递给 ThreadPoolExecutor 构造函数:
int poolSize = 1; // number of threads
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>();
Executor executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, queue);

当你在代码中清空队列时,剩余的任务将不会执行:

queue.clear();

2
Bombe的回答正是你想要的。 shutdownNow()使用“清空重建”的方法停止所有东西。这是除了子类化你正在使用的ThreadPoolExecutor实现之外,你可以做的最好的事情。

1
你可以尝试 allowCoreThreadTimeOut(true);

你应该发展你的回答,并表明你实际上正在回答问题。 - gniourf_gniourf

0
一个疯狂而不太干净的解决方案(没有经过深思熟虑或测试),是覆盖您的WorkerTasks的interrupt(),只有在设置了某些全局值时,才会在shutdownNow()调用它们的interrupt()时拒绝关闭。
这样应该可以让您使用shutdownNow(),对吧?

0
告诉你的线程池关闭,获取队列,使用for-each将结果转换为单个Runnable,使用remove方法删除每个Runnable。根据队列类型,您可能可以根据返回值尽早停止删除。
基本上,这是获取队列并清除它,仅通过有效的方法进行清除。您不必手动记住所有提交内容,而是利用线程池已经记住所有提交内容的事实。但是,您可能需要对队列进行防御性复制,因为我认为它是一个实时视图,因此如果您正在迭代/ for-eaching实时视图,则删除可能会导致并发修改异常。

0

awaitTermination(long timeout, TimeUnit unit)在shutdown之后不起作用吗?

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接