Future.cancel()并不能取消ScheduledExecutorService的计划执行

10

我正在安排一个任务:

ScheduledExecutorService dataService = Executors.newScheduledThreadPool(1);
Future<?> dataTimerHandle = dataService.scheduleAtFixedRate(runnable, 100, freq, TimeUnit.MILLISECONDS);

这段代码一开始能够正常运行。

但是,当用户操作中某个标志变为true时,任务不再需要定期执行,而只需执行一次。此时,我尝试按照以下方式取消任务并仅提交一次:

if(!dynamicUpdate) {
    dataTimerHandle.cancel(true); 
    dataTimerHandle = dataService.submit(runnable);
}
else { //Reschedule again:
    dataTimerHandle = dataService.scheduleAtFixedRate(runnable, 100, freq, TimeUnit.MILLISECONDS);
}

但似乎可运行对象仍按照周期执行,cancel()没有按预期工作。是否有其他替代策略?


请注意,只要重新/设置“dynamicUpdate”标志,调度就可以随时停止和恢复。 - abksrv
3个回答

4
问题可能不在Future的cancel()方法中。这里有一个小的可运行示例,看起来正好做到了你想要的:
import java.util.concurrent.*;

public class CancelPeriodicTask {

    public static void main(String[] args) {

        ScheduledThreadPoolExecutor scheduler = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
        scheduler.setRemoveOnCancelPolicy(true);
        try {
            new CancelPeriodicTask().test(scheduler);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            int openTasks = scheduler.shutdownNow().size();
            println("Finished, open tasks: " + openTasks);
            // openTasks will be 1 when RemoveOnCancelPolicy is false
            // and the executor is closed within the scheduled task-period.
        }
    }

    private static long sleepTime = 25L;

    public void test(ScheduledThreadPoolExecutor scheduler) throws Exception {

        // sleep 5 times at scheduled interval
        SleepTask sleepTask;
        ScheduledFuture<?> scheduledSleep = scheduler.scheduleAtFixedRate(sleepTask = new SleepTask(), 0, 2 * sleepTime, TimeUnit.MILLISECONDS);
        sleepTask.sleepTimes.await();
        println("Cancelling scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
        scheduledSleep.cancel(true);
        Thread.sleep(2 * sleepTime);
        println("Running sleepTask once.");
        scheduler.submit(sleepTask);
        Thread.sleep(2 * sleepTime);
        scheduledSleep = scheduler.scheduleAtFixedRate(sleepTask, 0, 2 * sleepTime, TimeUnit.MILLISECONDS);
        println("Re-scheduled scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
        Thread.sleep(5 * sleepTime);
        println("Cancelling scheduledSleep. Done: " + scheduledSleep.isDone() + ", cancelled: " + scheduledSleep.isCancelled());
        scheduledSleep.cancel(true);
    }

    class SleepTask implements Runnable {

        public final CountDownLatch sleepTimes = new CountDownLatch(5); 
        public int sleepCount;

        @Override public void run() {
            println("Sleeping " + (++sleepCount));
            try { Thread.sleep(sleepTime); } catch (Exception e) {
                e.printStackTrace();
            }
            sleepTimes.countDown();
        }
    }

    private static final long START_TIME = System.currentTimeMillis(); 

    private static void println(String msg) {
        System.out.println((System.currentTimeMillis() - START_TIME) + "\t " + msg);
    }

}

这真的很有帮助。我尝试在没有scheduler.setRemoveOnCancelPolicy(true)的情况下运行脚本,但没有发现任何区别。如果您也能帮忙解决此问题的副作用,那就太好了。另外,是否真的需要在每次取消后等待,就像您提到的Thread.sleep()一样,在重新安排之前?我不想等待可运行对象完成运行。 - abksrv
@abksrv 每次取消后都不需要等待,示例中只是为了显示任务不再计划。RemoveOnCancel 仅在任务从池中删除时起作用:通常它仅在下一次计划运行时从池中删除。 - vanOekel
2
谢谢!我只有一个抱怨。我认为 scheduler.setRemoveOnCancelPolicy(true); 在这里是关键因素。在我的情况下,当我执行 scheduledFuture.cancel(true) 后,我会得到一个不想要的执行。我非常确定应该强调 setRemoveOnCancel,并将答案标记为正确。 - igor.beslic

2
这是预期的,因为您正在向错误的句柄发送取消命令。当您调用service.submit()时,它会返回新创建的future的句柄,您不能使用完全相同的句柄向通过其他调用创建的future发送取消消息。
显然,您可以通过service.shutdown()关闭执行程序服务,以便在某个时刻之后不启动任何可运行项。

请参考上面编辑过的问题。我正在更新句柄。我不确定是否可以使用shutdown(),因为标志位'dynamicUpdate'可能随时变为true,调度程序必须在这种情况下进行相应的操作。 - abksrv
你似乎每次循环迭代都重新分配句柄,所以它不能工作。请发布更多的代码和更多的要求。 - Raffaele

0

我认为,future.cancel方法会中断正在运行的线程,因此您需要在可运行类中捕获InterruptedException并返回;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接