在ScheduledExecutorService中运行的任务本身内停止定期任务

时间:2011-02-05 21:34:47

标签: java concurrency anonymous-class executorservice variable-initialization

在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时,有一个潜在的窗口。)

5 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:4)

您可以使用一个命名类,而不是使用匿名内部类,该类可以为您在安排任务时从Future获取Executor对象的属性。

abstract class FutureRunnable implements Runnable {

    private Future<?> future;

    /* Getter and Setter for future */

}

当您安排任务时,您可以将Future传递给Runnable

FutureRunnable runnable = new FutureRunnable() {

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

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

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

答案 2 :(得分:2)

对于Runnable而言,它似乎是一个糟糕的设计,无法知道它正在运行的执行程序,或者如果达到10不是错误状态就会抛出错误。

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

答案 3 :(得分: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);

答案 4 :(得分: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是一个不可思议的内部类,它将完全在另一个线程中运行。