簡體   English   中英

如何識別被取消的 ScheduledFuture 是否真的沒有被取消?

[英]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時。 這里有兩種可能:

  1. 該任務實際上已被取消,並且 runnable 從未運行過。
  2. runnable 正在運行,因此無法取消。 (除非我做了一些我不想做的線程中斷邏輯)

我對這兩種情況的發生都沒有意見,但我想知道實際發生了哪一種並采取相應的行動。 如果 runnable 正在處理中,那么我可以接受它完成它的工作,我想等待它完成然后做一件事。 但如果它被取消並且根本不會運行,我想做另一件事。

你能推薦一種方法嗎? 我錯過了什么嗎?

您可以通過以下方式實現您的目標:

  • 一個Set<Runnable> ,用於跟蹤已開始由線程池執行的Runnable
  • 將 ScheduledFuture< ScheduledFuture<?> > 映射到其各自的RunnableMap<ScheduledFuture<?>, Runnable>
    • 調度任務后,您應該立即將ScheduledFuture及其各自的Runnable添加到Map中。
    • 如果這種插入Map的操作是通過調度任務本身以原子方式執行的,那么您可以避免ScheduledFuture即使在被取消后也從未添加到Map的邊緣情況。

我建議將您的ScheduledExecutorService更改為ScheduledThreadPoolExecutor ,這將允許您覆蓋其beforeExecute(Thread, Runnable)方法; 在已經為池分配了將執行任務的線程之后,池運行任務之前立即調用此方法。

覆蓋此方法時,您可以將Runnable添加到您的Set<Runnable>中。

然后,當ScheduledFuture被取消時,您可以調用set.contains(map.get(future)) ,它會告訴您RunnableScheduledFuture映射到的)是否已執行。


請注意,您的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM