16

在什么情况下希望将false参数mayInterruptIfRunning传递给Future.cancel()?

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

其他可能的情况是:

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

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

4

3 回答 3

20

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

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

首先是在 Java 中取消是纯粹的合作。Java 通过让阻塞方法抛出 InterruptedExcetions 并在线程上设置一个标志来发出取消请求的信号。任务实现负责通知取消请求并自行取消。Brian Goetz 在他的处理 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. 您愿意等待已经开始的任务完成。
于 2011-08-04T17:59:30.863 回答
9

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

编写正确处理中断的线程代码根本不是一件容易的事,因此人们可能更愿意避免它。

一些信息可以在这里找到,这里当然也可以在伟大的 Java 并发编程一书中找到(由最初编写的人编写java.util.concurrent)。

于 2010-07-17T13:35:57.030 回答
1

我有一个你可能会感兴趣的用例:我有一个线程正在执行一组计划任务。其中一个任务可以由其本身或另一个任务重新安排。

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

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

于 2015-12-08T23:27:30.247 回答