我正在使用 ScheduledExecutorService 并提交这样的任务:
future = scheduledExecutorService.schedule(myRunnableTask, delay, timeunit)
然而,某个事件可能会在不确定的时间后发生,这表明不再需要该任务。所以我需要取消这个任务,我正在使用
boolean canceled = future.cancel(false)
行。
取消后,我必须根据提交的可运行对象是否实际运行来采取不同的操作。让我们首先进入 Oracle 文档并阅读 cancelled
标志的含义:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(boolean)
Returns: false if the task could not be cancelled, typically because it has already completed normally; true otherwise
这就是关于返回值的全部内容。似乎编写此文本行的人不确定此处的 false
返回值,但我想我可以接受。
现在让我们关注案例,当它返回 true
时。这里有两种可能:
- 任务实际上被取消了,runnable 从未运行过。
- runnable 正在运行,因此无法取消。 (除非我做了一些我不想做的线程中断逻辑)
我可以接受这两种情况的发生,但我想知道实际发生了哪一种情况并采取相应的措施。如果 runnable 在进程中,那么我可以接受它完成它的工作,我想等待它完成然后做一件事。但如果它被取消并且根本不会运行,我想做另一件事。
你能推荐一种方法吗?我错过了什么吗?
最佳答案
您可以通过以下方式实现您的目标:
- A
Set<Runnable>
跟踪Runnable
已开始由线程池执行的 s。 - A
Map<ScheduledFuture<?>, Runnable>
映射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>
实现可能必须是线程安全的,以避免可能的竞争条件。
关于java - 如何识别被取消的 ScheduledFuture 是否真的没有被取消?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55922874/