【问题标题】:InvokeAll timeout interruptInvokeAll 超时中断
【发布时间】:2017-09-13 13:58:45
【问题描述】:

问题

如何让一系列因invokeAll 而超时的线程停止执行。

背景

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

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

所以一秒钟后它就会超时,我可以继续我的一天。然而,在后台线程仍然处于活动状态并可能执行一些代码。基本上它们不会被打断,这是我不想发生的——我希望线程停止执行。

invokeAll 超时后,有什么方法可以使用返回的期货列表来停止线程?

【问题讨论】:

  • 文档说,当返回的未完成的任务被取消时。 docs.oracle.com/javase/6/docs/api/java/util/concurrent/…
  • 我认为它们已被取消,但它们仍然有效,即使在 invokeAll 停止阻止后仍可以继续记录
  • 线程在做什么?线程不响应中断的原因有很多种(阻塞IO、代码吃掉中断、代码忽略中断、等待进入同步块……)

标签: java multithreading executorservice


【解决方案1】:

ExecutorService 旨在支持cancellation 响应interruption. 的任务因此,正确的方法是让您的Callable 实现注意到它们已被中断。

如果您的任务是纯粹的计算,或者如果它经常循环,调用第三方库,这很容易。在你的任务中,你会有一个看起来像这样的循环:

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

当然,您也可以在循环中测试其他特定于任务的条件,但您应该测试当前线程的中断状态。

调用interrupted() 将清除中断状态。调用Thread.currentThread().isInterrupted() 将保留中断状态。对于ExecutorService 中的工作线程,保持中断状态并不重要——服务正在中断自己的线程,当它的线程从您的任务返回时,服务会在不查看状态的情况下清除状态。但是,采用这是一个好习惯,这样如果将代码合并到任务中,它就会很好地工作。要在捕获InterruptedException 或用Thread.interrupted() 检测到它后重新断言中断状态,只需调用Thread.currentThread().interrupt()

如果任务是对不支持中断的第三方库的长时间调用,或者对不可中断 I/O 操作的阻塞调用,那么您的“任务”会困难得多。您可能完全不走运,或者您可能会找到一个异步中止操作的工具。

例如,如果您创建一个Socket,并将它(或其流)传递给您的任务,您可以异步关闭套接字。如果invokeAll 超时,调用者将需要额外的代码来关闭套接字。

【讨论】:

    【解决方案2】:

    你不能同时拥有它两种方式

    ExecutorService“框架”允许您只考虑Callable 对象和Future 结果。这种抽象隐藏所涉及线程的所有低级细节。因此:即使有可能,您的想法也很可能是“肮脏的黑客”。

    换句话说:如果您想要低级直接访问线程做某事 - 那么您可能必须使用自己的“线程池”实现。

    中间立场:请记住,这些线程执行您的 Callable/Runnable 实例。您当然可以想出一些办法,让这些对象定期检查某种“命令队列”。当队列包含 cancel 请求 - 那么 Callable 中的 your 代码将停止工作。

    【讨论】:

    • 您的评论很有道理,但当我给invokeAll 超时时,我真的不会认为这将是标准行为。非常奇怪的是,保持运行的线程被“有效地停止”但在您调用引发InterruptedException 的东西之前不会中断。应该有更高级别的方式来轮询中断的异常
    • 重点是:“中断”线程并不是你在 Java 中“通常”做的事情。当您长时间运行可运行/可调用代码时,您可以让该代码以某种方式检查一些“取消”标志。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多