В зависимости от того, хотите ли вы агрессивно очистить данные с истекшим сроком (для восстановления памяти) или вы просто хотите пересчитать после истечения срока действия, подход будет совсем другим.
Если вы просто хотите пересчитать, я бы расширил SoftReference, например:
public class ExpiringSoftReference<T> extends SoftReference<T> implements Serializable {
private final long _expirationMoment;
public ExpiringSoftReference(Object referent, long duration, TimeUnit unit) {
this(referent, System.currentTimeMillis() + unit.toMillis(duration);
}
private ExpiringSoftReference(Object referent, long expirationMoment) {
super(referent);
_expirationMoment = expirationMoment;
}
public T get() {
if (System.currentTimeMillis() >= _expirationMoment) {
clear();
}
return super.get();
}
private Object writeReplace() throws ObjectStreamException {
return new SerializedForm<T>(get(), _expirationMoment);
}
private static class SerializedForm<T> implements Serializable {
private final T _referent;
private final T _expirationMoment;
SerializedForm(T referent, long expirationMoment) {
_referent = referent;
_expirationMoment = expirationMoment;
}
private Object readResolve() throws ObjectStreamException {
return new ExpiringSoftReference<T>(_referent, _expirationMoment);
}
}
}
Если вы хотите активно восстанавливать память, вам нужно реализовать своего рода сборщик мусора. Подход заключается в том, чтобы сначала поместить все ссылки в очередь приоритетных потоков, а затем время от времени заглядывать в первый элемент, чтобы узнать, не истек ли хотя бы один из ссылок:
public class ExpiringSoftReference<T>
extends SoftReference<T>
implements Comparable<ExpiringSoftReference>, Serializable {
// same as above, plus
public int compareTo(ExpiringSoftReference other) {
if (this._expirationMoment < other._expirationMoment) {
return -1;
} else if (this._expirationMoment > other._expirationMoment) {
return 1;
} else {
return 0;
}
}
final long expiration() {
return _expirationMoment;
}
}
public class ExpirationEnforcer {
private final PriorityBlockingQueue<ExpiringSoftReference> _byExpiration = new ...();
public void add(ExpiringSoftReference reference) {
_byExpiration.put(reference);
}
public synchronized void tick() {
long now = System.currentTimeMillis();
while (true) {
ExpiringSoftReference candidate = _byExpiration.peek();
if (candidate == null || candidate.expiration() > now) {
return;
}
ExpirationSoftReference toClear = _byExpiration.peek();
toClear.clear();
}
}
}
Вам нужно будет вызывать tick () в очереди каждые пару секунд или что-то еще. Для этого в Java EE вам потребуется использовать службу таймера или что-то в этом роде.