Debian GNU/Linux Sarge で java memcached client を試す ということをやってみて、ファイルやDBはまだしも、メモリなら簡単だろうな、ということで叩き台を作ってみた。


import java.util.*;
 
/**
 * オブジェクトをメモリにキャッシュします。
 */
public final class MemoryCache {
 
  // test
  public static void main(String[] args){
 
    {
      MemoryCache mc = new MemoryCache(3);
      for(int i=0; i<10; i++){
        mc.set("" + i, "Data" + i, new Date().getTime() + (30 * i));
        try{Thread.sleep(100);}catch(Exception e){}
        Object data = mc.get("" + i);
        System.out.println(data);
      }
      for(int i=0; i<10; i++){
        Object data = mc.get("" + i);
        System.out.println(data);
      }
    }
  
    {
      MemoryCache mc = new MemoryCache(3);
      mc.debug = true;
      for(int i=0; i<10; i++){
        mc.set("" + i, "Data" + i, new Date().getTime() + (30 * i));
        try{Thread.sleep(100);}catch(Exception e){}
        Object data = mc.get("" + i);
        System.out.println(data);
      }
      for(int i=0; i<10; i++){
        Object data = mc.get("" + i);
        System.out.println(data);
      }
    }
  }
  
  private static final String FQCN = MemoryCache.class.getName();
  
  private final Map cdmap;
 
  public boolean debug;
 
  /**
   * @param capacity キャッシュ内に保存するオブジェクトの最大数(厳密ではない)
   */
  public MemoryCache(int capacity){
    // cdmap = Collections.synchronizedMap(new CacheRepository(this, capacity));
    cdmap = new CacheRepository(this, capacity);
  }
 
  private static class CacheRepository extends LinkedHashMap {
 
    private final MemoryCache cm;
    private final int capacity;
 
    private CacheRepository(MemoryCache cm, int capacity){
      super(capacity+1, 0.75f, true); // 順序モード=true(アクセス順)
      this.cm = cm;
      this.capacity = capacity;
    }
 
    protected boolean removeEldestEntry(Map.Entry eldest){
      boolean result = size() > capacity; // 削除する場合はtrueを返す
      if(result){
        // ここで削除されるオブジェクトをファイルやDBに保存できると良いなぁ
        cm.debug(FQCN + ": remove eldest cache data (" + ((CachedData)eldest.getValue()).value + ")"); // DEBUG
      }
      return result;
    }
  }
 
  private static class CachedData{
    public Object value;
    public long expiry;
  }
 
  /**
   * オブジェクトをキャッシュに入れます。
   * @param expiry オブジェクトの有効期限(Epoch Time からのミリ秒数), 有効期限なしの場合は負の値を指定
   */
  public synchronized void set(String key, Object value, long expiry){
    CachedData cd = new CachedData();
    cd.value = value;
    if(expiry < 0){
      expiry = Long.MAX_VALUE; // long の最大値を入れておけばラク
    }
    cd.expiry = expiry;
    cdmap.put(key, cd);
  }
 
  /**
   * オブジェクトをキャッシュから取り出します。
   * @param expiry オブジェクトの有効期限(Epoch Time からのミリ秒数), 有効期限なしの場合は負の値を指定
   */
  public synchronized Object get(String key){
    Object obj = cdmap.get(key);
    if(obj != null){
      CachedData cd = (CachedData)obj;
      if(new Date().getTime() > cd.expiry){
        // オブジェクトの有効期限切れ
        if(debug){
          debug(FQCN + ": remove expired cache data (" + cd.value + ")"); // DEBUG
        }
        cdmap.remove(key);
        return null;
      }else{
        return cd.value;
      }
    }else{
      return null;
    }
  }
 
  void debug(Object x){
    if(debug){
      System.out.println(x);
    }
  }
  
}

当然、このつくりでは JVM 起動中しかメモリにキャッシュされない。

tags: zlashdot Java Java

Posted by NI-Lab. (@nilab)