我有关于ThreadPoolExecutor这个相当简单的问题。我有以下情况:我必须从队列中使用对象,为它们创建适当的工作人员任务并将它们提交给ThreadPoolExecutor。这很简单。但是在关闭场景中许多工作人员可能会排队等待执行。由于其中一个任务可能运行了一个小时,并且我希望相对快速正常关闭应用程序,我想从ThreadPoolExecutor中丢弃所有排队的任务,而已处理的任务应该正常完成。
ThreadPoolExecutor文档具有remove()方法,但只允许删除特定任务。 purge()仅适用于已取消的Future任务。我的想法是清除队列中包含所有排队的任务。 ThreadPoolExecutor提供对此内部队列的访问,但文档说明:
方法getQueue()允许访问 用于监视的工作队列 和调试。使用这种方法 出于任何其他目的是强烈的 气馁。
因此抓住这个队列并清除它不是一个选择。此外,该文档的片段说:
两种提供的方法, remove(java.lang.Runnable)和purge() 可以协助存储 大量的回收 排队的任务被取消。
如何?当然,我可以维护我提交给执行程序的所有任务的列表,在关闭的情况下,我遍历所有条目并使用remove()方法将它们从ThreadPoolExecutor中删除......但是...来吧,这是一个浪费记忆力和维护这份清单的麻烦。 (例如,删除已执行的任务)
我感谢任何提示或解决方案!
答案 0 :(得分:13)
我曾经在使用长线程的应用上工作。我们在关机时执行此操作,
BlockingQueue<Runnable> queue = threadPool.getQueue();
List<Runnable> list = new ArrayList<Runnable>();
int tasks = queue.drainTo(list);
列表将保存到文件中。在启动时,列表会被添加回池中,因此我们不会丢失任何作业。
答案 1 :(得分:10)
您是否考虑过包装ExecutorService?创建一个
CleanShutdownExecutorService implements Executor
将所有调用委托给另一个Executor,但将Futures保留在自己的列表中。然后,CleanShutdownExecutorService可以使用cancelRemainingTasks()方法调用shutdown(),然后在其列表中的所有Futures上调用cancel(false)。
答案 2 :(得分:4)
由于ExecutorService.shutdown()做得不够而ExecutorService.shutdownNow()做得太多,我猜你必须在中间写一些东西:记住所有提交的任务,并在调用之后(或之前)手动删除它们shutdown()
。
答案 3 :(得分:3)
这是一个老问题,但是如果这有助于其他人:你可以在调用shutdown()时设置一个volatile布尔值,并且如果在真正启动之前设置了布尔值,则让每个提交的任务终止。这将允许真正开始完成的任务,但会阻止排队任务开始实际活动。
答案 4 :(得分:1)
Bombe的答案正是你想要的。 shutdownNow()
使用 nuke和pave 方法停止所有操作。这是你能做的最好的事情,除了你正在使用的ThreadPoolExecutor
的实现的子类化。
答案 5 :(得分:1)
您可以尝试allowCoreThreadTimeOut(true);
答案 6 :(得分:0)
一个疯狂且不干净的解决方案可能会工作(不经过深思熟虑或经过测试)将覆盖您的WorkerTasks的interrupt()
,只有在某些全局值设置为interrupt()
时拒绝关闭的情况下由shutdownNow()调用它们。
这应该允许您使用shutdownNow()
否?
答案 7 :(得分:0)
告诉你的线程池关闭,getQueue,for-each结果到单个Runnables,使用remove方法删除每个Runnable。根据队列的类型,您可以根据返回值提前暂停删除。
基本上,这是抓住队列并清除它,只通过有效的方法清除。您可以使用线程池已经记住所有提交的事实,而不是手动记住所有提交。但是,您可能需要制作队列的防御副本,因为我认为它是实时视图,因此如果您在实时视图上进行迭代/ for-eaching,则删除可能会导致并发修改异常。
答案 8 :(得分:0)
关机后awaitTermination(long timeout, TimeUnit unit)
不起作用吗?
executor.shutdown(); executor.awaitTermination(60,TimeUnit.SECONDS)
答案 9 :(得分:0)
您可以创建自己的任务队列并将其传递给ThreadPoolExecutor
构造函数:
int poolSize = 1; // number of threads
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>();
Executor executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, queue);
当您清除代码中的队列时,其余任务将不会执行:
queue.clear();