我有一个抽象类,用于使子类的实例到期:
public abstract class Expirable {
private transient Timer timer;
protected abstract void onExpire();
protected void setExpire(long delay) {
resetExpire();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
resetExpire();
onExpire();
}
}, delay
);
}
protected void resetExpire() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
我扩展任何类并覆盖onExpire
,然后从子类(未显示)调用setExpire(delay)
:
public class MyClass extends Expirable {
@Override
protected void onExpire() {
// expiration code here
}
}
该类工作得很好,但是timer
对象非常昂贵。因为我有成千上万的实例,所以我编写了一个具有相同功能的廉价版本,该版本使用以固定速率安排的单个timer
和queue
。不够精确,但是便宜。
public abstract class ExpirableCheap {
private static final long COLLECTOR_INTERVAL_MSEC = 1000;
private static final int INITIAL_CAPACITY = 128;
private static Queue<ExpirableCheap> queue = new PriorityBlockingQueue<>(
INITIAL_CAPACITY,
Comparator.comparingLong(expirable -> expirable.expiresWhen)
);
@SuppressWarnings("FieldCanBeLocal")
private static TimerTask timerTask;
@SuppressWarnings("FieldCanBeLocal")
private static Timer timer;
static {
timerTask = new TimerTask() {
@Override
public void run() {
// this Runnable stops working
long time = new Date().getTime();
while (queue.peek() != null && queue.peek().expiresWhen < time) {
queue.poll().onExpire();
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(timerTask,
COLLECTOR_INTERVAL_MSEC,
COLLECTOR_INTERVAL_MSEC
);
}
private transient long expiresWhen;
protected abstract void onExpire();
protected synchronized void setExpire(long delay) {
resetExpire();
long time = new Date().getTime();
expiresWhen = time + delay;
queue.offer(this);
}
protected synchronized void resetExpire() {
queue.remove(this);
}
}
很明显,静态代码块执行一次,并以固定间隔调度timer
。 timerTask
窥视队列并呼叫onExpire()
。
怎么了?
这可以运行一会儿,但是突然timerTask
不再执行。在测试时,它工作正常,我无法模拟这种情况,但是在生产一段时间后失败了。
我不确定会发生什么,但是我怀疑在收集子类的最后一个实例时,我在静态代码块中初始化的静态变量是垃圾回收。然后,当重新使用该类时,将不再运行静态代码块。换句话说,它似乎可以工作,直到extend ExpirableCheap
不再存在实例为止。
奇怪的是,queue
仍然存在,这是为什么我希望Runnable
内部发生异常的原因,我认为事实并非如此。
如您所见,我尝试将timer
和timerTask
变量从静态代码块移到成员变量中(这没有帮助)。我还尝试同步setExpire()
和resetExpire()
,我认为这也没有什么区别。
有人可以看到发生了什么吗?我犯了另一个愚蠢的错误,我走错了路吗?
有什么建议我可以做些什么来使它起作用?
答案 0 :(得分:0)
正如@TimBiegeleisen正确指出的那样,Java可以按预期工作。当子类的最后一个实例被收集时,抽象超类不会被垃圾收集。
我的问题无关。