是否有一种有效的方法在Java中使用重置功能实现倒数计时器?
我正在将事件发送到服务器。如果一段时间没有事件发生,我需要发送一个心跳信号,以便服务器知道我们还活着。事件的发送频率必须低于每200毫秒。
理想情况下,我有一个倒数计时器,我设置为200毫秒。每次发生事件时,我都会将计时器重置为200毫秒。如果计时器超时,它将发送心跳并自行重置。
我不想为每个计时器绑定一个线程,因为我可能需要其中几个。我不想使用Timer + TimerTask,因为每次重置都需要创建一个新的TimerTask并将其添加到队列中。在重负载下,我可能每秒要发射几千个事件,并且不想为每个事件创建一个TimerTask。
我提出的最佳方案是使用Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(...);
并每10或20毫秒启动一次。我可以检查每个滴答的倒计时值并根据需要重置它。但这并不是非常有效,并且不会给我一个好的计时器分辨率。
有更好的方法吗?
答案 0 :(得分:2)
您可以使用Object.wait(long)
和Object.notify()
滚动自己的自定义计时器。
public abstract class ResettableTimer implements Runnable {
private Object lock = new Object();
private long timeout_ms;
private long last;
public ResettableTimer(long timeout_ms) {
this.timeout_ms = timeout_ms;
}
public void reset() {
synchronized (lock) {
last = System.currentTimeMillis();
lock.notify();
}
}
@Override
public void run() {
try {
synchronized (lock) {
for (;;) {
lock.wait(timeout_ms);
long delta = System.currentTimeMillis() - last;
if (delta >= timeout_ms) {
timeout();
}
}
}
} catch (InterruptedException e) {
}
}
protected abstract void timeout();
}
创建一个子类,覆盖timeout()
以发送心跳消息。每次发送消息时,请调用ResettableTimer#reset()
方法重置心跳计时器。
希望有所帮助。
答案 1 :(得分:1)
使用ExecutorService是有道理的。 Goetz等人认为它比 Java Concurrency in Practice 中的util.Timer有所改进。
我会考虑以下内容:
当TimerTask发生时,如果 lastEventTime 是>过去200毫秒,发送ping并将 lastEventTime 变量更新到当前,然后创建并设置一个新的TimerTask以再次检查新的 lastEventTime + 200(其中将来将是200毫秒)。
如果在TimerTask检入时,过去发生的事件少于200毫秒(例如过去150毫秒),请将下一个TimerTask设置为在当前 lastEventTime +处再次检入200(未来50毫秒,在示例中)。