![](/img/trans.png)
[英]ThreadPoolExecutor runs even after ScheduledFuture is cancelled
[英]How to identify if cancelled ScheduledFuture is actually not cancelled?
我正在使用 ScheduledExecutorService 并提交这样的任务:
future = scheduledExecutorService.schedule(myRunnableTask, delay, timeunit)
然而,某个事件可能会在不确定的时间后发生,这表明不再需要该任务。 所以我需要取消这个任务,我正在使用
boolean cancelled = future.cancel(false)
行。
取消后,我必须根据提交的 runnable 是否实际运行来采取不同的操作。 在这里让我们首先跳转到 Oracle 文档并阅读cancelled
标志的含义:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(布尔值)
返回:如果任务无法取消,则返回 false,通常是因为它已经正常完成; 否则为真
这就是关于返回值的全部内容。 似乎写这行文本的人不确定这里的false
返回值,但我想我可以接受。
现在让我们关注案例,当它返回true
时。 这里有两种可能:
我对这两种情况的发生都没有意见,但我想知道实际发生了哪一种并采取相应的行动。 如果 runnable 正在处理中,那么我可以接受它完成它的工作,我想等待它完成然后做一件事。 但如果它被取消并且根本不会运行,我想做另一件事。
你能推荐一种方法吗? 我错过了什么吗?
您可以通过以下方式实现您的目标:
Set<Runnable>
,用于跟踪已开始由线程池执行的Runnable
。ScheduledFuture<?>
> 映射到其各自的Runnable
的Map<ScheduledFuture<?>, Runnable>
。
ScheduledFuture
及其各自的Runnable
添加到Map
中。Map
的操作是通过调度任务本身以原子方式执行的,那么您可以避免ScheduledFuture
即使在被取消后也从未添加到Map
的边缘情况。 我建议将您的ScheduledExecutorService
更改为ScheduledThreadPoolExecutor
,这将允许您覆盖其beforeExecute(Thread, Runnable)
方法; 在已经为池分配了将执行任务的线程之后,池运行任务之前立即调用此方法。
覆盖此方法时,您可以将Runnable
添加到您的Set<Runnable>
中。
然后,当ScheduledFuture
被取消时,您可以调用set.contains(map.get(future))
,它会告诉您Runnable
( ScheduledFuture
映射到的)是否已执行。
请注意,您的Set<Runnable>
和Map<ScheduledFuture<?>, Runnable>
实现可能必须是线程安全的,以避免可能的竞争条件。
我最终为这个问题写了这样的东西。 源代码和一些单元测试可以在https://github.com/nuzayats/cancellabletaskexecutor找到
public class CancellableTaskExecutor {
private final ScheduledExecutorService es;
private final Logger log;
/**
* For a unit test to replicate a particular timing
*/
private final Runnable hookBetweenCancels;
public CancellableTaskExecutor(ScheduledExecutorService es, Logger log) {
this(es, log, () -> {
// nop
});
}
// For unit tests
CancellableTaskExecutor(ScheduledExecutorService es, Logger log, Runnable hookBetweenCancels) {
this.es = es;
this.log = log;
this.hookBetweenCancels = hookBetweenCancels;
}
public Execution schedule(Runnable task, long delay, TimeUnit unit) {
CancellableRunnable runnable = new CancellableRunnable(task);
ScheduledFuture<?> future = es.schedule(runnable, delay, unit);
return new Execution(future, runnable);
}
public class Execution {
private final ScheduledFuture<?> future;
private final CancellableRunnable runnable;
private Execution(ScheduledFuture<?> future, CancellableRunnable runnable) {
this.future = future;
this.runnable = runnable;
}
/**
* @return true when the task has been successfully cancelled and it's guaranteed that
* the task won't get executed. otherwise false
*/
public boolean cancel() {
boolean cancelled = runnable.cancel();
hookBetweenCancels.run();
// the return value of this call is unreliable; see https://stackoverflow.com/q/55922874/3591946
future.cancel(false);
return cancelled;
}
}
private class CancellableRunnable implements Runnable {
private final AtomicBoolean cancelledOrStarted = new AtomicBoolean();
private final Runnable task;
private CancellableRunnable(Runnable task) {
this.task = task;
}
@Override
public void run() {
if (!cancelledOrStarted.compareAndSet(false, true)) {
return; // cancelled, forget about the task
}
try {
task.run();
} catch (Throwable e) {
log.log(Level.WARNING, "Uncaught Exception", e);
}
}
boolean cancel() {
return cancelledOrStarted.compareAndSet(false, true);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.