31

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

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

方法 getQueue() 允许访问工作队列以进行监视和调试。强烈建议不要将此方法用于任何其他目的。

所以抓住这个队列并清除它不是一种选择。此外,这个文档片段说:

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

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

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

4

10 回答 10

14

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

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

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

于 2009-11-05T15:46:15.693 回答
11

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

CleanShutdownExecutorService implements Executor 

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

于 2009-11-05T16:00:23.003 回答
5

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

于 2009-11-05T10:18:30.300 回答
5

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

于 2010-11-10T18:59:12.110 回答
3

You could create your own task queue and pass it to ThreadPoolExecutor constructor:

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

When you clear the queue somewhere in your code then the remaining tasks will not be executed:

queue.clear();
于 2018-09-18T12:50:57.197 回答
2

Bombe 的答案正是您想要的。使用核武器和铺路shutdownNow()方法停止一切。这是您可以做的最好的事情,没有子类化您正在使用的实现。ThreadPoolExecutor

于 2009-11-05T12:59:20.817 回答
1

你可以试试allowCoreThreadTimeOut(true);

于 2010-10-27T09:07:38.020 回答
0

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

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

于 2009-11-05T16:05:06.583 回答
0

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

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

于 2009-11-05T13:46:00.490 回答
0

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

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

于 2009-11-05T13:19:10.330 回答