调用InvokeAll超时中断

5

问题:

如何使用invokeAll创建一系列具有超时的线程并停止执行。

背景:

我有一个callables列表,我希望等待它们完成。但是,如果它们在设定的时间(比如一秒)内没有完成,我想要取消它们。这似乎很容易通过使用

executor.invokeAll(callables, 1000l, TimeUnit.MILLISECONDS);

所以,等待一秒钟后,它会很好地超时,我就可以继续我的工作了。然而,在后台,线程仍然活跃,并且可能执行某些代码片段。本质上,它们没有被中断,而我不希望发生这种情况 - 我希望线程停止执行。

有没有办法使用返回的futures列表,在invokeAll超时后停止线程?


文档说明返回未完成的任务将被取消。http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html#invokeAll%28java.util.Collection,%20long,%20java.util.concurrent.TimeUnit%29 - pshirishreddy
我认为它们已经被取消了,但它们仍然处于活动状态,并且即使在“invokeAll”停止阻塞后也可以继续记录。 - DWB
线程在做什么?有各种可能的原因导致线程不响应中断(阻塞IO、代码吃掉中断、代码忽略中断、等待进入同步块...) - bowmore
2个回答

1

ExecutorService 的设计目的是支持对响应取消中断的任务进行取消。因此,正确的方法是使您的Callable实现注意到它们已被中断。

如果您的任务纯粹是计算性的,或者经常循环调用第三方库,那么这很容易。在您的任务中,您将有一个类似下面的循环:

while (!Thread.interrupted()) {
  /* Keep going */
}

当然,您也可以在循环中测试其他特定于任务的条件,但应该测试当前线程的中断状态。调用interrupted()将清除中断状态。调用Thread.currentThread().isInterrupted()将保留中断状态。对于ExecutorService中的工作线程,保持中断状态并不太重要——服务正在中断自己的线程,并且当其线程从您的任务返回时,服务会清除状态而不查看它。但是,养成这种习惯很好,这样如果代码被合并到任务中,它将能够正常工作。在捕获InterruptedException或使用Thread.interrupted()检测到它后重新断言中断状态,只需调用Thread.currentThread().interrupt()即可。
如果任务是调用不支持中断的第三方库或阻塞的不可中断I/O操作,则你的“任务”将更加困难。你可能完全没有运气,或者你可以找到一个异步中止操作的折衷方法。
例如,如果你创建了一个Socket,并将其(或其流)传递给你的任务,你可以异步关闭这个Socket。如果invokeAll超时,调用者需要额外的代码来关闭Socket。

1

你不能两者兼得。

ExecutorService“框架”使您只需要考虑Callable对象和Future结果。这种抽象将涉及的所有线程的低级细节隐藏起来。因此:即使可能,您的想法很可能是一个“脏技巧”。

换句话说:如果您想要对执行某些操作的线程进行低级别、直接的访问 - 那么您可能需要使用自己的“线程池”实现。

中间地带:请记住,这些线程执行您的 Callable/Runnable 实例。当然,您可以想出一些东西,让那些对象定期检查某种“命令队列”。当队列包含取消请求时 - 的 Callable 中的代码停止执行其工作。


你的评论很有道理,但我真的不会假设当我给invokeAll设置超时时,这将是标准行为。非常奇怪的是,保持运行的线程“实际上被停止”,但在调用引发InterruptedException的内容之前并没有被中断。应该有一种更高级别的方式来轮询中断异常。 - DWB
重点是:“中断”线程不是Java中“常见”的操作。当您有长时间运行的Runnable/Callable代码时,那么您需要让该代码以某种方式检查一些“取消”标志。 - GhostCat

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