【问题标题】:Use case for Future.cancel(false)?Future.cancel(false) 的用例?
【发布时间】:2010-07-17 12:59:59
【问题描述】:

在什么情况下希望将falsemayInterruptIfRunning 参数传递给@987654321@

如果我理解正确,如果你通过false 并且任务被取消但线程没有被中断,结果(或ExecutionException)将永远无法访问,因为任务仍然标记为已取消(即isCancelled() 返回trueget() 抛出CancellationException。)

其他可能的情况有:

  • RunnableCallable 实现不检查中断,即使您确实中断它也会运行完成(这里中断没有区别)
  • 在您调用 cancel() 之前任务已经完成(同样中断没有区别)
  • 任务需要在退出之前执行一些清理(编写良好的实现将为此使用try ... finally。)
  • 任务不能立即终止,必须继续执行受中断影响的操作,例如阻塞 I/O(在这种情况下,您可能根本不应该调用 cancel

那么你什么时候/为什么要取消一个任务而不打断它呢?

【问题讨论】:

    标签: java concurrency future


    【解决方案1】:

    tl;博士; Future.cancel(false) 仅用于避免启动尚未启动的任务。

    关于并发和取消,有两件重要的事情需要了解。

    首先是在 Java 中取消是纯粹的合作。 Java 通过让阻塞方法抛出 InterruptedExcetions 并在线程上设置一个标志来发出取消请求的信号。任务实现负责通知取消请求并自行取消。 Brian Goetz 在他的帖子 Dealing with InterruptedException 中解释了中断。并非所有任务实现都能正确处理中断。

    要指出的第二件事是,Future 对象是将来要执行的任务结果的占位符。如果您没有运行很多线程,则任务可能会立即开始执行,但也可能所有线程都已被使用并且任务必须等待。仅仅因为你有一个 Future 对象的引用并不意味着相应的任务实际上已经开始运行。有点像预订。

    您有一个 Future 对象,但任务可能处于以下状态之一:

    1. 等待。例如,它可能在等待处理器时间的其他任务队列中。
    2. 正在运行。
    3. 已完成。

    如果您的任务处于“等待”的第一个状态,那么 Future.cancel(true)Future.cancel(false) 都会将未来标记为已取消。该任务仍保留在要执行的任务队列中,但是当执行程序到达该任务时,它会注意到已取消的标志并跳过它。

    如果您的任务处于“已完成”的第三种状态,则 Future.cancel(true)Future.cancel(false) 都将返回 false 并且不执行任何操作。这是有道理的,因为它们已经完成并且没有办法撤消它们。

    mayInterruptIfRunning 标志仅在您的任务处于第二个状态“正在运行”时才重要。

    如果您的任务正在运行并且mayInterruptIfRunning 为假,则执行程序不会执行任何操作并允许任务完成。

    如果您的任务正在运行并且mayInterruptIfRunning 为真,那么执行器将中断该任务。但请记住关于合作取消的一点——要使中断工作,必须实施任务来处理取消。

    总结:

    Future.cancel(true) 适用于:

    1. Future 代表一个长期运行的任务,已知该任务已被实施以处理中断。

    Future.cancel(false) 是正确的:

    1. 任务实现无法处理被中断。
    2. 不知道任务实现是否支持取消。
    3. 您愿意等待已经开始的任务完成。

    【讨论】:

    • 很好的总结。很遗憾,从cancel()的返回值我们无法区分是Waiting还是Running。
    • @antak 我曾经需要类似的东西并扩展了 FutureTask。在 ctor 中,您使用自己的 callable 包装 Callable 参数,在调用原始 callable.call() 之前设置 wasStarted 标志
    • 是的,尽管需要注意同步,因为在提交启动任务的执行程序和我们的代码设置 wasStarted 之间有一个关键部分。
    • @antak 不是。您正在用自己的包装原始可调用对象。当您的可调用对象被调用时,执行程序已经为您提供了一个要执行的线程。实现设置标志并调用原始可调用对象。没有什么可以介于两者之间。原始可调用对象可能会检查中断或取消并立即退出,但 wasStarted 标志仍然有效。可调用对象已启动。
    • 是的,但是你调用cancel(...),它会返回true。你不知道它是 Waiting 还是 Running,所以你看一下 wasStarted,它是 false。这仍然不排除“正在运行”(即任务不会运行/尚未运行),因为您可能处于临界区,或者变量可能根本不会被传播(如果它不是 volatile) . cancel(false)cancel(true) 都需要特定类型的同步来确定绝对原始可调用对象是已启动还是永远不会启动。
    【解决方案2】:

    如果您担心中断任务的执行可能会使事情处于不良状态,并且您只想将其标记为已取消,以便Future 的用户会意识到这一点(例如,他们应该知道请求的统计信息没有按时执行)。

    编写正确处理中断的线程代码并非易事,因此人们可能更愿意避免它。

    一些信息可以在herehere 中找到,当然也可以在伟大的书Concurrent Programming in Java 中找到(作者是最初写java.util.concurrent 的人)。

    【讨论】:

    • 您的第一句话不太正确。如果任务正在运行或完成,则 cancel(false) 不会将其标记为已取消。
    【解决方案3】:

    我有一个您可能会感兴趣的用例:我有一个正在执行一组计划任务的线程。其中一项任务可以自行重新安排,也可以由另一项任务重新安排。

    为此,我在队列中的现有副本上使用Future.cancel(false),然后将任务安排到新的时间。

    如果从计划任务本身调用此代码,则取消操作将是空操作,并且该任务将被计划在将来再次运行。如果代码是从另一个上下文中调用的,那么即将到来的任务还没有开始执行,所以它会被取消,并替换为一个为新时间安排的任务。

    【讨论】:

      猜你喜欢
      • 2011-08-14
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      • 2018-12-07
      • 2019-06-16
      • 2018-07-25
      • 1970-01-01
      相关资源
      最近更新 更多