从 ScheduledExecutorService 中运行的任务本身停止周期性任务

发布于 2024-10-16 04:52:11 字数 523 浏览 10 评论 0原文

在 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。)

Is there a nice way to stop the repetition of task from within the task itself when running in a ScheduledExecutorService?

Lets say, I have the following task:

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);

From outside, it is easy to cancel via f.cancel(), but how can I stop the repetition at the specified place? (Passing the Future through an AtomicReference is not safe, because there is a potential window when the scheduleAtFixedRate returns f late and the variable is set late too, and the task itself might already run, seeing a null in the reference.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不知在何时 2024-10-23 04:52:11

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

When a repeating task throws an Exception or Error, it is placed in the Future and the task is not repeated again. You can throw a RuntimeException or Error of your choice.

爱给你人给你 2024-10-23 04:52:11

您可以使用命名类,而不是使用匿名内部类,该类可以具有您在计划任务时从 Executor 获取的 Future 对象的属性。

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

Instead of using an anonymous inner class you can use a named class which can then have a property for the Future object you get from the Executor when you schedule a task.

abstract class FutureRunnable implements Runnable {

    private Future<?> future;

    /* Getter and Setter for future */

}

When you schedule a task you can then pass the Future to the Runnable.

FutureRunnable runnable = new FutureRunnable() {

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

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

Maybe you will have to make sure, that the task is not executed before the Future has been set, because otherwise you will get a NullPointerException.

牵你的手,一向走下去 2024-10-23 04:52:11

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

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

It seems like bad design for the Runnable to know anything about the executor it is running in, or to throw an error if reaching 10 is not an error state is a hack.

Can you do the loop to 10 outside of the scheduling and execution? This may require using a non-scheduling executor as you'd be scheduling them manually yourself.

野心澎湃 2024-10-23 04:52:11

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

    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);

Here is another way, that's even Thread safe;

    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);
风启觞 2024-10-23 04:52:11

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

首先为 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);
    }
}

然后是 future 代码:

    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);
    }
}

所以请注意,在创建 future 并在控制器上设置 future 之前,将无法调用 stop。

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

Just saw this now... because I wanted to do the same thing... here is my solution, I suspect this is threadsafe.

First create a container for the 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);
    }
}

And then the future code:

    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);
    }
}

So notice how the stop will not be callable until the future has been created and the future has been set on the controller.

Bear in mind that the Runnable is the anomymous inner class and this will get run in a different thread altogether.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文