Stop a periodic task from within the task itself running in a ScheduledExecutorService

送分小仙女□ 提交于 2019-11-27 21:13:15

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.

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.

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.

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!