Java 我们如何在应用程序范围中存储数据以进行缓存?

Java 我们如何在应用程序范围中存储数据以进行缓存?,java,jsp,caching,web-applications,Java,Jsp,Caching,Web Applications,我正在应用程序范围中存储数据。我想在每小时后清除这些数据。事实上,将其作为缓存使用一小时。实现这一点的最佳方式是什么?早些时候,我们使用会话作用域存储此数据,并且在会话过期后,它会过期。由于此数据在整个应用程序中是唯一的,因此我们希望将其存储在应用程序范围中。根据您是想积极清除过期数据(以恢复内存)还是只想在过期时间后重新计算,方法会大不相同 如果您只是想重新计算,我将扩展SoftReference,例如: public class ExpiringSoftReference<T>

我正在应用程序范围中存储数据。我想在每小时后清除这些数据。事实上,将其作为缓存使用一小时。实现这一点的最佳方式是什么?早些时候,我们使用会话作用域存储此数据,并且在会话过期后,它会过期。由于此数据在整个应用程序中是唯一的,因此我们希望将其存储在应用程序范围中。

根据您是想积极清除过期数据(以恢复内存)还是只想在过期时间后重新计算,方法会大不相同

如果您只是想重新计算,我将扩展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);
    }
  }
}
公共类过期SoftReference扩展SoftReference实现可序列化{
私人最终长期到期时刻;
公共ExpiringSoftReference(对象引用、长持续时间、时间单位){
这(referent,System.currentTimeMillis()+unit.toMillis(duration);
}
私有ExpiringSoftReference(对象引用,长expirationMoment){
超级(参照物);
_expirationMoment=expirationMoment;
}
公共部门得不到{
if(System.currentTimeMillis()>=\u expirationMoment){
清除();
}
返回super.get();
}
私有对象writeReplace()抛出ObjectStreamException{
返回新的SerializedForm(get(),_expirationMoment);
}
私有静态类SerializedForm实现可序列化{
私人最终T_指代人;
私人最终T_到期时刻;
SerializedForm(T referent,long expirationMoment){
_referent=referent;
_expirationMoment=expirationMoment;
}
私有对象readResolve()引发ObjectStreamException{
返回新的ExpiringSoftReference(_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();
    }
  }
}
公共类过期SoftReference
扩展软引用
实现可比较的、可序列化的{
//同上,加上
公共int比较(过期软件引用其他){
如果(本到期时刻<其他到期时刻){
返回-1;
}否则如果(本.\u到期时刻>其他.\u到期时刻){
返回1;
}否则{
返回0;
}
}
最终长期到期(){
返回-到期时刻;
}
}
公共类过期强制执行器{
private final PriorityBlockingQueue_byExpiration=new…();
公共作废添加(ExpiringSoftReference引用){
_按到期日。放置(参考);
}
公共同步的void tick(){
long now=System.currentTimeMillis();
while(true){
ExpiringSoftReference候选者=_byExpiration.peek();
if(candidate==null | | candidate.expiration()>now){
返回;
}
ExpirationSoftReference toClear=_byExpiration.peek();
toClear.clear();
}
}
}

您需要每隔几秒钟或任何时间对队列调用tick()。要在Java EE中执行此操作,您需要使用计时器服务或类似的服务。

对于非常简单的缓存,您可以使用Google guava的: 以下是取自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);
             }
           });
ConcurrentMap graphs=newmapmaker()
.1级(4)
.软键()
.weakValues()
.最大尺寸(10000)
.expireAfterWrite(10,时间单位:分钟)
.makeComputingMap(
新函数(){
公共图形应用(密钥){
返回createExpensiveGraph(键);
}
});

有两种方法可以让你做你想做的事情。你可以免费得到一个线程安全的映射,如果你想/需要它,可以使用弱值、软键和惰性评估:)

这些数据来自哪里?如果从一个数据库,你应该考虑使用ORM支持第一级和第二级缓存,如Hibernate /JPA。通过这种方式,您不需要为了从缓存中依赖而更改代码(这将使其很难维护/重用)。您只需以通常的方式启动SQL查询,Hibernate/JPA将在适当的时候从Java的内存缓存中返回对象

另见:

我们找到了一种使用ServletContextListener和计时器清除应用程序范围变量的方法

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
    }       
}   
公共类SampleListener实现ServletContextListener{
公共静态最终长时间_startTimerDelayMin=10;//分钟
公共静态最终长时间_startTimerPeriodMin=60;//分钟
定时器;
ServletContext=null;
public void contextInitialized(ServletContextEvent){
context=contextEvent.getServletContext();
scheduleTimer();
}   
public void scheduleTimer(){
长延迟=_startTimerDelayMin*60000;//初始延迟设置为_startTimerDelayMin分钟,以毫秒计
long period=\u startTimerPeriodMin*60000;//后续速率设置为每毫秒_startTimerPeriodMin分钟
定时器=新定时器();
timer.scheduleAtFixedRate(新的提醒任务(),延迟,周期);
}
公共无效上下文已销毁(ServletContextEvent arg0){
//一旦上下文被破坏,就停止计时器线程
timer.cancel();
}
私有静态字符串getPropertyValue(字符串键){
字符串值=”;
试一试{
value=Util.getPropertyValueOfKey(key.trim();
}捕获(IOE异常){
}
返回值;
}
/**
*在给定的时间间隔调用此类,以清除在给定时间内未使用的浏览调用的应用程序范围变量
*
*/
类任务扩展TimerTask{
公开募捐{
变量();
} 
/**
*此函数具有清除在给定时间内未使用的浏览调用的应用程序范围变量的逻辑
*/
公共vo