Java 在时间间隔内运行上次设置的任务

Java 在时间间隔内运行上次设置的任务,java,java-threads,Java,Java Threads,标题可能有点不对劲,所以这里有一个扩展的问题: 我有一个用户控件,例如按钮。每当我点击按钮时,一个昂贵的Runnable应该在ScheduledExecutorService中调度。因为Runnable运行一些昂贵的代码,所以我的想法是,如果在给定的时间间隔内再次按下按钮,则只运行所述Runnable。如果在所述时间间隔内再次按下该按钮,则定时器应复位,且相同的可运行应在给定延迟后运行。如果在延迟间隔期间未再次按下按钮,则执行Runnable 是否存在某种内置方式,或者我是否能够以某种方式意识到

标题可能有点不对劲,所以这里有一个扩展的问题:

我有一个用户控件,例如
按钮
。每当我点击按钮时,一个昂贵的
Runnable
应该在
ScheduledExecutorService
中调度。因为
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
时,只要在
x+300
x+800
期间未再次单击按钮,调度程序应异步运行方法
doSomething()

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

正如我之前所问的,是否有一种方法可以通过使用
ScheduledExecutorService
正确地实现这一点

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更改,则在执行之前会出现一个新任务,因此不应启动该任务


希望这对您有用:)

每当您计划一些操作时,您都会收到
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);
});
private ScheduledFuture任务;
按钮。设置操作(事件->{
如果(任务!=null){
//如果要取消已在运行的任务,请将此更改为true
任务。取消(false);
}
task=scheduler.schedule(()->doSomething(),500,TimeUnit.ms);
});

你能解释一下原子整数的作用吗?我不太明白那个角色。代码工作得很好!由于不同的线程可以访问
RepeatedCallScheduler
,因此
taskNo
充当全局id存储,告诉调度器要执行哪些任务和跳过哪些任务。它应该是一个
AtomicInteger
,以使其线程安全,这意味着每个调用都将获得一个唯一的id。我没有想过这样使用它。尽管它工作得非常好,就像下面的答案一样,我会将下面的一个标记为已接受,因为它更接近我当前的实现。谢谢你的帮助!