Depending on whether you want to aggressively clean up expired data (to restore memory) or just want to re-install after expiration, the approach will be completely different.
If you just want to double-check, I would distribute SoftReference, for example:
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); } } }
If you want to aggressively restore memory, you need to implement some sort of garbage collection. The approach should be to first put all links in the thread safety priority queue, and then look at the first element to see if at least one of the links has expired:
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(); } } }
You will need to call tick () in the queue every couple of seconds or something else. For this, in Java EE you will need to use a timer service or something like that.
source share