在时间间隔中运行最后一组任务

时间:2022-10-28 17:33:54

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.

我有一个用户控件,例如Button。每当我单击该按钮时,都应在ScheduledExecutorService中安排昂贵的Runnable。因为Runnable运行了一些昂贵的代码,所以如果按钮在给定的时间间隔内没有被按下另一次,我只想运行所说的Runnable。如果在所述间隔内再次按下该按钮,则应该重置计时器,并且在给定的延迟之后应该运行相同的Runnable。如果在延迟间隔期间没有再次按下该按钮,则执行Runnable。

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.

在此示例中,时间延迟值为500毫秒,这意味着最后一次单击按钮后500毫秒应该运行方法doSomething()。

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.

我按时按钮(以毫秒为单位)x,第二次点击时间x + 300.现在第一次点击事件不应该运行,但是在时间x + 800,调度程序应该异步运行方法doSomething(),如只要在x + 300和x + 800期间未再次单击该按钮。

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

在此之后,程序打印“hello”一次,而不是两次。

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

正如我之前所问,有没有办法通过使用ScheduledExecutorService来正确实现它?

2 个解决方案

#1


1  

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.

您可以使用容器包装要执行的代码并为其指定ID。如果全局ID发生更改,则在执行之前会出现一个新任务,并且不应该启动它。

Hope this works for you :)

希望这适合你:)

#2


1  

Whenever you schedule some action you receive ScheduledFuture instance which you can use to cancel previous task and schedule new one:

每当您安排某些操作时,您都会收到ScheduledFuture实例,您可以使用该实例取消上一个任务并安排新任务:

private ScheduledFuture<?> task;

button.setOnAction(event -> {
    if (task != null) {
        // change this to true if you want to cancel already running task
        task.cancel(false);
    }
    task = scheduler.schedule(() -> doSomething(), 500, TimeUnit.MILLISECONDS);
});

#1


1  

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.

您可以使用容器包装要执行的代码并为其指定ID。如果全局ID发生更改,则在执行之前会出现一个新任务,并且不应该启动它。

Hope this works for you :)

希望这适合你:)

#2


1  

Whenever you schedule some action you receive ScheduledFuture instance which you can use to cancel previous task and schedule new one:

每当您安排某些操作时,您都会收到ScheduledFuture实例,您可以使用该实例取消上一个任务并安排新任务:

private ScheduledFuture<?> task;

button.setOnAction(event -> {
    if (task != null) {
        // change this to true if you want to cancel already running task
        task.cancel(false);
    }
    task = scheduler.schedule(() -> doSomething(), 500, TimeUnit.MILLISECONDS);
});