[英]How to identify if cancelled ScheduledFuture is actually not cancelled?
I'm using a ScheduledExecutorService and submitting a task like that:我正在使用 ScheduledExecutorService 并提交这样的任务:
future = scheduledExecutorService.schedule(myRunnableTask, delay, timeunit)
However a certain event might occur after indefinite amount of time, which signals that this task is no longer needed.然而,某个事件可能会在不确定的时间后发生,这表明不再需要该任务。 And so I need to cancel this task, and I am using
所以我需要取消这个任务,我正在使用
boolean cancelled = future.cancel(false)
line. boolean cancelled = future.cancel(false)
行。
After cancelling, I have to take different actions depending on whether the submitted runnable actually ran or not.取消后,我必须根据提交的 runnable 是否实际运行来采取不同的操作。 And here lets first jump into Oracle Documentation and read what
cancelled
flag means:在这里让我们首先跳转到 Oracle 文档并阅读
cancelled
标志的含义:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(boolean) https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(布尔值)
Returns: false if the task could not be cancelled, typically because it has already completed normally;
返回:如果任务无法取消,则返回 false,通常是因为它已经正常完成; true otherwise
否则为真
That's all it says about the return value.这就是关于返回值的全部内容。 Seems like the person who wrote this text line was uncertain about
false
return value here, but I think I can take it.似乎写这行文本的人不确定这里的
false
返回值,但我想我可以接受。
Lets now focus on the case, when it returns true
.现在让我们关注案例,当它返回
true
时。 There are two possibilities here:这里有两种可能:
I am okay with both cases occurring, but I want to KNOW which one actually occurred and take actions accordingly.我对这两种情况的发生都没有意见,但我想知道实际发生了哪一种并采取相应的行动。 If the runnable is in the process, then I am okay with it finishing it's job, I want to wait for it's completion and then do one thing.
如果 runnable 正在处理中,那么我可以接受它完成它的工作,我想等待它完成然后做一件事。 But if it was cancelled and never going to run at all, I want to do another thing.
但如果它被取消并且根本不会运行,我想做另一件事。
Can you please recommend an approach to this?你能推荐一种方法吗? Am I missing something?
我错过了什么吗?
You can accomplish your goal with the following:您可以通过以下方式实现您的目标:
Set<Runnable>
that keeps track of Runnable
s that have begun execution by the thread pool.Set<Runnable>
,用于跟踪已开始由线程池执行的Runnable
。Map<ScheduledFuture<?>, Runnable>
that maps a ScheduledFuture<?>
to its respective Runnable
.ScheduledFuture<?>
> 映射到其各自的Runnable
的Map<ScheduledFuture<?>, Runnable>
。
ScheduledFuture
and its respective Runnable
to the Map
.ScheduledFuture
及其各自的Runnable
添加到Map
中。Map
is performed atomically with scheduling the task itself, then you can avoid the edge case that the ScheduledFuture
was never added to the Map
even after it was cancelled.Map
的操作是通过调度任务本身以原子方式执行的,那么您可以避免ScheduledFuture
即使在被取消后也从未添加到Map
的边缘情况。 I recommend changing your ScheduledExecutorService
to a ScheduledThreadPoolExecutor
, which will allow you to override its beforeExecute(Thread, Runnable)
method;我建议将您的
ScheduledExecutorService
更改为ScheduledThreadPoolExecutor
,这将允许您覆盖其beforeExecute(Thread, Runnable)
方法; this method is invoked immediately before the task is run by the pool after it has already been assigned a thread that will execute the task.在已经为池分配了将执行任务的线程之后,池运行任务之前立即调用此方法。
When overriding this method, you can add the Runnable
to your Set<Runnable>
.覆盖此方法时,您可以将
Runnable
添加到您的Set<Runnable>
中。
Then, when a ScheduledFuture
is cancelled, you can call set.contains(map.get(future))
, which tells you if the Runnable
(that the ScheduledFuture
maps to) was executed.然后,当
ScheduledFuture
被取消时,您可以调用set.contains(map.get(future))
,它会告诉您Runnable
( ScheduledFuture
映射到的)是否已执行。
Note that your Set<Runnable>
and Map<ScheduledFuture<?>, Runnable>
implementations may have to be made thread-safe to avoid possible race conditions.请注意,您的
Set<Runnable>
和Map<ScheduledFuture<?>, Runnable>
实现可能必须是线程安全的,以避免可能的竞争条件。
I ended up writing something like this for this issue.我最终为这个问题写了这样的东西。 Source code and some unit tests can be found at https://github.com/nuzayats/cancellabletaskexecutor
源代码和一些单元测试可以在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.