22

在 ScheduledExecutorService 中运行时,是否有一种很好的方法可以从任务本身中停止重复任务?

可以说,我有以下任务:

Future<?> f = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
    int count = 0;
    public void run() {
       System.out.println(count++);
       if (count == 10) {
           // ??? cancel self
       }
    }
}, 1, 1, TimeUnit.SECONDS);

从外面看,很容易通过 f.cancel() 取消,但是我怎样才能在指定的地方停止重复呢?(通过 AtomicReference 传递 Future 是不安全的,因为当 scheduleAtFixedRate 返回 f 并且变量设置也很晚时存在一个潜在的窗口,并且任务本身可能已经运行,在引用中看到 null。)

4

5 回答 5

14

当重复的任务抛出异常或错误时,它被放置在 Future 中,并且不再重复该任务。您可以抛出您选择的 RuntimeException 或 Error。

于 2011-02-05T21:47:03.117 回答
5

您可以使用命名类而不是使用匿名内部类,然后该类可以为您在安排任务时Future获得的对象提供属性。Executor

abstract class FutureRunnable implements Runnable {

    private Future<?> future;

    /* Getter and Setter for future */

}

当您安排任务时,您可以将 传递FutureRunnable.

FutureRunnable runnable = new FutureRunnable() {

    public void run() {
        if (/* abort condition */)
            getFuture().cancel(false);
    }

};
Future<?> future = executor.scheduleAtFixedRate(runnable, ...);
runnable.setFuture(future);

也许您必须确保在Future设置之前未执行任务,否则您将获得NullPointerException.

于 2011-02-05T23:59:02.417 回答
2

对于 Runnable 来说,了解有关它正在运行的执行器的任何信息似乎是糟糕的设计,或者如果达到 10 不是错误状态则抛出错误是一种黑客行为。

你能在调度和执行之外循环到 10 吗?这可能需要使用非调度执行器,因为您将自己手动调度它们。

于 2011-02-05T22:03:05.043 回答
1

这是另一种方式,甚至是线程安全的;

    final Future<?>[] f = {null};
    f[0]=  scheduledExecutor.scheduleAtFixedRate(new Runnable() {
        int count = 0;
        public void run() {

            System.out.println(count++);
            if (count == 10) {
                Future<?> future;
                while(null==(future = f[0])) Thread.yield();//prevent exceptionally bad thread scheduling 
                future.cancel(false);
                return;
                //cancel self
            }
        }
    }, 1, 1, TimeUnit.SECONDS);
于 2011-02-06T00:24:05.593 回答
1

现在才看到这个......因为我想做同样的事情......这是我的解决方案,我怀疑这是线程安全的。

首先为 Future 创建一个容器:

public static class Cancel {
    private ScheduledFuture<?> future;

    public synchronized void setFuture(ScheduledFuture<?> future) {
        this.future = future;
    }

    public synchronized void stop() {
        LOG.debug("cancelling {}", future);
        future.cancel(false);
    }
}

然后是未来的代码:

    final Cancel controller = new Cancel();

    synchronized (controller) {
        ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(() -> {
            if (<CONTINUE RUNNING CONDITION) {

            } else {
                // STOP SCHEDULABLE FUTURE
                controller.stop();
            }
        }, startTime, timeBetweenVisbilityChecks);
        controller.setFuture(future);
    }
}

所以请注意,在创建未来并且在控制器上设置未来之前,停止是如何被调用的。

请记住,Runnable 是异常的内部类,它将完全在不同的线程中运行。

于 2015-04-30T10:56:08.507 回答