标题可能有点偏离,所以这是扩展的问题:
我有一个用户控件,例如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 + 300
和x + 800
期间未再次单击该按钮即可。
此后程序打印"你好" 一次,而不是两次。
正如我之前所问,有没有办法使用ScheduledExecutorService
正确实现这个?
答案 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);
});