对于我正在编写的一些代码,我可以使用debounce
在Java语言中的一个很好的通用实现。
public interface Callback {
public void call(Object arg);
}
class Debouncer implements Callback {
public Debouncer(Callback c, int interval) { ... }
public void call(Object arg) {
// should forward calls with the same arguments to the callback c
// but batch multiple calls inside `interval` to a single one
}
}
当使用相同的参数在interval
毫秒内多次调用call()
时,回调函数应该只调用一次。
可视化:
Debouncer#call xxx x xxxxxxx xxxxxxxxxxxxxxx
Callback#call x x x (interval is 2)
发布于 2014-01-08 02:22:38
请考虑以下线程安全解决方案。请注意,锁的粒度是在键级别上的,因此只有对同一键的调用才会相互阻塞。它还处理在调用call(K)时发生的密钥K过期的情况。
public class Debouncer <T> {
private final ScheduledExecutorService sched = Executors.newScheduledThreadPool(1);
private final ConcurrentHashMap<T, TimerTask> delayedMap = new ConcurrentHashMap<T, TimerTask>();
private final Callback<T> callback;
private final int interval;
public Debouncer(Callback<T> c, int interval) {
this.callback = c;
this.interval = interval;
}
public void call(T key) {
TimerTask task = new TimerTask(key);
TimerTask prev;
do {
prev = delayedMap.putIfAbsent(key, task);
if (prev == null)
sched.schedule(task, interval, TimeUnit.MILLISECONDS);
} while (prev != null && !prev.extend()); // Exit only if new task was added to map, or existing task was extended successfully
}
public void terminate() {
sched.shutdownNow();
}
// The task that wakes up when the wait time elapses
private class TimerTask implements Runnable {
private final T key;
private long dueTime;
private final Object lock = new Object();
public TimerTask(T key) {
this.key = key;
extend();
}
public boolean extend() {
synchronized (lock) {
if (dueTime < 0) // Task has been shutdown
return false;
dueTime = System.currentTimeMillis() + interval;
return true;
}
}
public void run() {
synchronized (lock) {
long remaining = dueTime - System.currentTimeMillis();
if (remaining > 0) { // Re-schedule task
sched.schedule(this, remaining, TimeUnit.MILLISECONDS);
} else { // Mark as terminated and invoke callback
dueTime = -1;
try {
callback.call(key);
} finally {
delayedMap.remove(key);
}
}
}
}
}
和回调接口:
public interface Callback<T> {
public void call(T t);
}
发布于 2016-07-11 04:09:24
下面是我的实现:
public class Debouncer {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final ConcurrentHashMap<Object, Future<?>> delayedMap = new ConcurrentHashMap<>();
/**
* Debounces {@code callable} by {@code delay}, i.e., schedules it to be executed after {@code delay},
* or cancels its execution if the method is called with the same key within the {@code delay} again.
*/
public void debounce(final Object key, final Runnable runnable, long delay, TimeUnit unit) {
final Future<?> prev = delayedMap.put(key, scheduler.schedule(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} finally {
delayedMap.remove(key);
}
}
}, delay, unit));
if (prev != null) {
prev.cancel(true);
}
}
public void shutdown() {
scheduler.shutdownNow();
}
}
示例用法:
final Debouncer debouncer = new Debouncer();
debouncer.debounce(Void.class, new Runnable() {
@Override public void run() {
// ...
}
}, 300, TimeUnit.MILLISECONDS);
发布于 2011-01-20 08:20:04
我不知道它是否存在,但它应该很容易实现。
class Debouncer implements Callback {
private CallBack c;
private volatile long lastCalled;
private int interval;
public Debouncer(Callback c, int interval) {
//init fields
}
public void call(Object arg) {
if( lastCalled + interval < System.currentTimeMillis() ) {
lastCalled = System.currentTimeMillis();
c.call( arg );
}
}
}
当然,这个示例有点过于简化了,但这或多或少就是您所需要的。如果您想为不同的参数保持单独的超时,那么您将需要一个Map<Object,long>
而不仅仅是一个long
来跟踪最后一次执行时间。
https://stackoverflow.com/questions/4742210
复制相似问题