猿问

如何识别已取消的 ScheduledFuture 是否实际上未取消?

我正在使用 ScheduledExecutorService 并提交这样的任务:

future = scheduledExecutorService.schedule(myRunnableTask, delay, timeunit)

然而,某个事件可能会在不确定的时间后发生,这表明不再需要此任务。所以我需要取消这个任务,我正在使用

boolean cancelled = future.cancel(false)线。

取消后,我必须根据提交的可运行文件是否实际运行来采取不同的操作。在这里,让我们首先进入 Oracle 文档并阅读cancelled标志的含义:

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(boolean)

返回:如果任务无法取消,则返回 false,通常是因为它已经正常完成;否则为真

这就是关于返回值的全部内容。似乎写这个文本行的人不确定false这里的返回值,但我想我可以接受。

现在让我们专注于案例,当它返回时true。这里有两种可能:

  1. 该任务实际上已被取消并且 runnable 从未运行过。

  2. 可运行对象正在运行,因此无法取消。(除非我做了一些线程中断逻辑,我真的不想这样做)

我对这两种情况都没有意见,但我想知道实际发生了哪一种情况并采取相应的行动。如果runnable正在处理中,那么我可以接受它完成它的工作,我想等待它完成然后做一件事。但如果它被取消并且根本不会运行,我想做另一件事。

你能推荐一种方法吗?我错过了什么吗?


红糖糍粑
浏览 88回答 2
2回答

料青山看我应如是

您可以通过以下方式实现您的目标:ASet<Runnable>跟踪Runnable线程池已开始执行的 s。AMap<ScheduledFuture<?>, Runnable>将 a 映射ScheduledFuture<?>到其各自的Runnable.安排任务后,您应立即将ScheduledFuture及其各自添加Runnable到Map.如果在调度任务本身的情况下以原子方式执行插入Map,那么您可以避免即使在取消之后也ScheduledFuture从未添加到的边缘情况。Map我建议将您更改ScheduledExecutorService为 a&nbsp;ScheduledThreadPoolExecutor,这将允许您覆盖其beforeExecute(Thread, Runnable)方法;这个方法在任务被池运行之前立即调用,在它已经被分配了一个将执行任务的线程之后。覆盖此方法时,您可以Runnable将Set<Runnable>.然后,当 aScheduledFuture被取消时,您可以调用set.contains(map.get(future)),它会告诉您Runnable(ScheduledFuture映射到的)是否已执行。请注意,您的Set<Runnable>和Map<ScheduledFuture<?>, Runnable>实现可能必须是线程安全的,以避免可能的竞争条件。

千万里不及你

我最终为这个问题写了这样的东西。源代码和一些单元测试可以在https://github.com/nuzayats/cancellabletaskexecutor找到public class CancellableTaskExecutor {&nbsp; &nbsp; private final ScheduledExecutorService es;&nbsp; &nbsp; private final Logger log;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* For a unit test to replicate a particular timing&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private final Runnable hookBetweenCancels;&nbsp; &nbsp; public CancellableTaskExecutor(ScheduledExecutorService es, Logger log) {&nbsp; &nbsp; &nbsp; &nbsp; this(es, log, () -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // nop&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; }&nbsp; &nbsp; // For unit tests&nbsp; &nbsp; CancellableTaskExecutor(ScheduledExecutorService es, Logger log, Runnable hookBetweenCancels) {&nbsp; &nbsp; &nbsp; &nbsp; this.es = es;&nbsp; &nbsp; &nbsp; &nbsp; this.log = log;&nbsp; &nbsp; &nbsp; &nbsp; this.hookBetweenCancels = hookBetweenCancels;&nbsp; &nbsp; }&nbsp; &nbsp; public Execution schedule(Runnable task, long delay, TimeUnit unit) {&nbsp; &nbsp; &nbsp; &nbsp; CancellableRunnable runnable = new CancellableRunnable(task);&nbsp; &nbsp; &nbsp; &nbsp; ScheduledFuture<?> future = es.schedule(runnable, delay, unit);&nbsp; &nbsp; &nbsp; &nbsp; return new Execution(future, runnable);&nbsp; &nbsp; }&nbsp; &nbsp; public class Execution {&nbsp; &nbsp; &nbsp; &nbsp; private final ScheduledFuture<?> future;&nbsp; &nbsp; &nbsp; &nbsp; private final CancellableRunnable runnable;&nbsp; &nbsp; &nbsp; &nbsp; private Execution(ScheduledFuture<?> future, CancellableRunnable runnable) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.future = future;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.runnable = runnable;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* @return true when the task has been successfully cancelled and it's guaranteed that&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* the task won't get executed. otherwise false&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; public boolean cancel() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; boolean cancelled = runnable.cancel();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hookBetweenCancels.run();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // the return value of this call is unreliable; see https://stackoverflow.com/q/55922874/3591946&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; future.cancel(false);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return cancelled;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; private class CancellableRunnable implements Runnable {&nbsp; &nbsp; &nbsp; &nbsp; private final AtomicBoolean cancelledOrStarted = new AtomicBoolean();&nbsp; &nbsp; &nbsp; &nbsp; private final Runnable task;&nbsp; &nbsp; &nbsp; &nbsp; private CancellableRunnable(Runnable task) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.task = task;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public void run() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!cancelledOrStarted.compareAndSet(false, true)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; // cancelled, forget about the task&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; task.run();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } catch (Throwable e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.log(Level.WARNING, "Uncaught Exception", e);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; boolean cancel() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return cancelledOrStarted.compareAndSet(false, true);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Java
我要回答