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

时间:2018-01-10 20:25:47

标签: java java-threads

标题可能有点偏离,所以这是扩展的问题:

我有一个用户控件,例如Button。只要我点击该按钮,就应该在Runnable中安排昂贵的 ScheduledExecutorService。由于Runnable会运行一些昂贵的代码,因此如果按钮在 期间再次按下,则我只想运行所述Runnable给定的时间间隔。如果在所述间隔内再次按下该按钮,则应重置计时器,并且在给定的延迟之后应该运行相同的Runnable。如果在延迟间隔期间没有再次按下该按钮,则执行Runnable

是否有一些内置方式或者我能以某种方式实现这一点吗?

目前的实现如下:

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

}

示例

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

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

此后程序打印"你好" 一次,而不是两次。

正如我之前所问,有没有办法使用ScheduledExecutorService正确实现这个?

2 个答案:

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

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

希望这适合你:)

答案 1 :(得分:1)

每当您安排某项操作时,您都会收到ScheduledFuture个实例,您可以将其用于cancel上一个任务并安排新任务:

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