代码之家  ›  专栏  ›  技术社区  ›  finnw

未来的用例。取消(错误)?

  •  12
  • finnw  · 技术社区  · 14 年前

    在什么情况下你会想通过 false 对于 mayInterruptIfRunning 参数到 Future.cancel() ?

    如果我理解正确,如果你通过 任务被取消,但线程 中断,结果(或 ExecutionException )永远无法访问,因为任务仍标记为已取消(即 isCancelled() 收益率 true get() 投掷 CancellationException )

    其他可能的情况包括:

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

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

    3 回复  |  直到 9 年前
        1
  •  7
  •   abyx    14 年前

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

    编写能够正确处理中断的线程代码一点也不简单,因此人们可能只希望避免它。

    可以找到一些信息 here , here 当然,在这本伟大的书中 Concurrent Programming in Java (由最初写信的人写的) java.util.concurrent )

        2
  •  9
  •   Ryan    9 年前

    TL;DR; Future.cancel(false) 只对避免启动尚未启动的任务有用。

    对于并发性和取消,有两件重要的事情需要理解。

    第一个是Java取消是纯合作的。Java通过取消阻塞方法、中断中断和在线程上设置标志来发出取消请求。任务实现负责通知取消请求并自行取消。布赖恩·戈茨解释了他的职位被打断的原因 Dealing with InterruptedException . 并非所有的任务实现都能正确地处理中断。

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

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

    1. 等待。例如,它可能在等待处理器时间的其他任务队列中。
    2. 跑步。
    3. 完整的。

    如果任务处于第一状态“等待”,则两者都处于 Future.cancel(true) 未来。取消(错误) 将把未来标记为已取消。任务仍在要执行的任务队列中,但当执行者到达任务时,它会注意到取消的标志并跳过它。

    如果任务处于“已完成”的第三种状态,则两种状态都不相同 未来。取消(真) 未来。取消(错误) 返回false,不要做任何事情。这是有意义的,因为它们已经完成了,而且没有一种方法可以撤销它们。

    这个 mayInterruptIfRunning 只有当任务处于第二状态“运行”时,标志才是重要的。

    如果任务正在运行并且 可能会中断工作 如果为假,则执行器不做任何事情,并允许任务完成。

    如果任务正在运行并且 可能会中断工作 如果为真,则执行器将中断任务。但是请记住关于合作取消的一点——对于工作中断,必须执行任务来处理取消。

    总结:

    未来。取消(真) 适用于以下情况:

    1. 未来表示一个长期运行的任务,已知该任务已被实现以处理中断。

    未来。取消(错误) 是正确的:

    1. 任务实现无法处理被中断的情况。
    2. 如果任务实现支持取消,则未知。
    3. 您愿意等待已开始的任务完成。
        3
  •  1
  •   Jeremy Salwen    9 年前

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

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

    如果从计划任务本身调用此代码,则取消操作将是一个“不操作”,并且该任务将被计划在将来再次运行。如果代码是从另一个上下文调用的,则即将到来的任务尚未开始执行,因此它将被取消,并替换为计划在新时间执行的任务。