Search code examples
javajava-threads

Run last set Task in time interval


The headline is probably a bit off, so here's the expanded question:

I have an user control, for example a Button. Whenever I click the button, an expensive Runnable should be scheduled in an ScheduledExecutorService. Because the Runnable runs some expensive code, I had the idea to only run the said Runnable if the button was not pressed another time during a given time interval. If the button was pressed again within the said interval, the timer should be reset and the same Runnable should be running after a given delay. If the button has not been pressed another time during the delay interval, the Runnable is executed.

Is there some build-in way or can I realize this somehow?

The current implementation looks like this:

public class RepeatedCallScheduler {

    private long waitForMillis;

    private long systemTimeMillis;

    public RepeatedCallScheduler(long waitForMillis) {
        this.waitForMillis = waitForMillis;
    }

    public void run(Runnable runnable) {
        this.systemTimeMillis = System.currentTimeMillis();

        // Run logic
    }

    public static void main(String[] args) {
        RepeatedCallScheduler scheduler = new RepeatedCallScheduler(500);

        Button button = new Button();
        button.setOnAction(event -> {
            scheduler.run(() -> doSomething());
        });
    }

    private static void doSomething() {
        System.out.println("hello");
    }

}

Example:

In this example, the time delay values 500 milliseconds, meaning 500 milliseconds after the last click on the button the method doSomething() should run.

I click the button on time (in milliseconds) x and the second time I click it at time x + 300. Now the first click event should not run but at time x + 800 the scheduler should run the method doSomething() asynchronously, as long as the button is not clicked again during x + 300 and x + 800.

After this the program prints "hello" once, not twice.

As I asked before, is there a way to properly implement this with the use of a ScheduledExecutorService?


Solution

  • private long waitForMillis;
    
    private AtomicInteger taskNo;
    
    private ScheduledExecutorService executorService;
    
    public RepeatedCallScheduler(long waitForMillis) {
        this.waitForMillis = waitForMillis;
        this.taskNo = new AtomicInteger();
        executorService = Executors.newScheduledThreadPool(4); // Whatever you need
    }
    
    public void run(Runnable runnable) {
    
        int no = taskNo.incrementAndGet();
    
            executorService.schedule(() -> {
                // Check if the task should be executed
                if (no == taskNo.get()) {
                    // Logic.. 
                }
            }, waitForMillis, TimeUnit.MILLISECONDS);
    }
    

    You could wrap the code to be executed with a container and give it an id. If the global id changed, a new task came in before execution and it should not be started.

    Hope this works for you :)