2 回答

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

TA贡献1784条经验 获得超9个赞
我最终为这个问题写了这样的东西。源代码和一些单元测试可以在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);
}
}
}
添加回答
举报