我在应用程序范围内存储数据。我想在每小时后清除这些数据。实际上使用它作为缓存一小时。实现这个的最佳方法是什么?之前我们使用会话范围来存储这些数据,并且在会话到期后它过去会过期。由于此数据在整个应用程序中是唯一的,因此我们希望将其存储在应用程序范围内。
答案 0 :(得分:5)
对于非常简单的缓存,您可以使用Google番石榴MapMaker: 以下是从javadoc中获取的示例:
ConcurrentMap<Key, Graph> graphs = new MapMaker()
.concurrencyLevel(4)
.softKeys()
.weakValues()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.makeComputingMap(
new Function<Key, Graph>() {
public Graph apply(Key key) {
return createExpensiveGraph(key);
}
});
有两种方法 expireAfterWrite 和 expireAfterRead 来做你想要的事情。而且如果你想要/需要它,你可以免费获得一个线程安全的Map,包含weakValue,软键和延迟评估:)
答案 1 :(得分:1)
根据您是要积极清除过期数据(恢复内存)还是只想在到期时间后重新计算,这种方法会有很大不同。
如果您只是想重新计算,我会扩展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中执行此操作,您需要使用计时器服务或类似的东西。
答案 2 :(得分:0)
这些数据来自哪里?如果来自数据库,则应考虑使用支持Hibernate / JPA等一级和二级缓存的ORM。这样,您不需要更改代码以便从缓存中进行依赖(这将使其难以维护/可重用)。您只需按常规方式触发SQL查询,Hibernate / JPA将在适当的时候从Java的内存缓存中返回对象。
答案 3 :(得分:0)
我们找到了一种使用ServletContextListener和Timer清除应用程序范围变量的方法。
public class SampleListener implements ServletContextListener {
public static final long _startTimerDelayMin = 10; // In minutes
public static final long _startTimerPeriodMin = 60; // In minutes
Timer timer;
ServletContext context =null;
public void contextInitialized(ServletContextEvent contextEvent) {
context = contextEvent.getServletContext();
scheduleTimer();
}
public void scheduleTimer() {
long delay = _startTimerDelayMin * 60000; //initial delay set to _startTimerDelayMin minutes as per msecs
long period = _startTimerPeriodMin * 60000; //subsequent rate set to _startTimerPeriodMin minutes as per msecs
timer = new Timer();
timer.scheduleAtFixedRate(new RemindTask(), delay, period);
}
public void contextDestroyed(ServletContextEvent arg0) {
//Stopping Timer Thread once context destroyed
timer.cancel();
}
private static String getPropertyValue(String key){
String value = "";
try{
value = Util.getPropertyValueOfKey(key).trim();
}catch(IOException e){
}
return value;
}
/**
* This class is invoked at given interval to clear the application scope variable for browse call which have not been used for given time
*
*/
class RemindTask extends TimerTask {
public void run() {
clearScopeVariables();
}
/**
* This function has logic to clear the application scope variable for browse call which have not been used for given time
*/
public void clearScopeVariables() {
Date dt = new Date();
Enumeration<String> applicationScopeVarNames = (Enumeration<String>)context.getAttributeNames();
// TODO: clear the scope variables
}
}
在web.xml中添加监听器