java實現本地緩存

/**
 * @author : hilite
 * @version : 1.0
 * @Title : LocalCache
 * @Description :
 * @date : 2019/9/18
 * @CopyRight : Copyright (c) 2015-2019 All Rights Reserved.
 * @Compony : hilite.cn Inc.
 */
public class LocalCache {

   private static final Logger logger = LoggerFactory.getLogger(LocalCache.class);
   //默認緩存容量
   private static final int DEFAULT_CAPACITY = 500;
   //最大緩存容量
   private static final int MAX_CAPACITY = 1000000;
   //監控清除過期緩存頻率
   private static final int MONITOR_FREQUENCY = 3;

   //構建本地緩存的map
   private static ConcurrentHashMap<String, CacheEntity> cache = new ConcurrentHashMap<String, CacheEntity>(DEFAULT_CAPACITY);

   //監控線程啓動
   static{
      new Thread(new MonitorThread()).start();
   }

   //監控線程
    static class MonitorThread implements Runnable{
      @Override
      public void run() {
         while(true){
            try {
               logger.info("START CACHE MONITOR");
               TimeUnit.SECONDS.sleep(MONITOR_FREQUENCY);
               checkTime();
            }catch (Exception e){
               logger.error("MONITOR CACHE HAS THROWS AN EXCEPTION:", e);
            }
         }
      }

      //過期key剔除
      private void checkTime(){
         cache.entrySet().stream().forEach(item -> {
            CacheEntity value = item.getValue();
            if(value.getExpireTime() != -1 && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - value.getGmtCreate()) > value.getExpireTime()){
               String key = item.getKey();
               cache.remove(key);
               logger.info("REMOVE EXPIRE KEY:{}", key);
            }
         });

      }
   }

   /**
    * 將key-value 保存到本地緩存並設置緩存過期時間
    * @param key
    * @param value
    * @param expireTime 過期時間,如果是-1 則表示永不過期
    * @return
    */
   public boolean put(String key, Object value, int expireTime){
      if(cache.size() >= MAX_CAPACITY){
         throw new RuntimeException("CAPACITY OVERFLOW!");
      }
      return putCloneValue(key, value, expireTime);
   }

   /**
    *將值通過序列化 clone 處理後保存到緩存中,可以解決值引用的問題
    *@param key
    *@param value
    *@param expireTime 過期時間,如果是-1 則表示永不過期
    *@return boolean
    */
   private boolean putCloneValue(String key, Object value, int expireTime){
      try {
         CacheEntity cacheEntity = clone(new CacheEntity(value, System.nanoTime(), expireTime));
         /*CacheEntity cacheEntity = (CacheEntity)javassist();
         cacheEntity.setObj(value);
         cacheEntity.setExpireTime(expireTime);
         cacheEntity.setGmtCreate(System.nanoTime());*/
         cache.put(key, cacheEntity);
         return true;
      }catch (Exception e){
         logger.error("PUT VALUE HAS THROWS AN EXCEPTION:", e);
         return false;
      }
   }

   /**
    * javassist測試
    */
   public <T extends Serializable> T javassist() throws Exception {
      ClassPool classPool = ClassPool.getDefault();
      CtClass ctClass = classPool.makeClass("com.zaxxer.hikari.cacheLocal.CacheEntity1");
      CtField ctField1 = new CtField(classPool.get("java.lang.Object"), "value", ctClass);
      CtField ctField2 = new CtField(CtClass.longType, "gmtCreate", ctClass);
      CtField ctField3 = new CtField(CtClass.intType, "expireTime", ctClass);
      CtField ctField4 = new CtField(CtClass.intType, "serialVersionUID", ctClass);
      ctField1.setModifiers(Modifier.PRIVATE);
      ctField2.setModifiers(Modifier.PRIVATE);
      ctField3.setModifiers(Modifier.PRIVATE);
      ctField4.setModifiers(Modifier.PRIVATE);
      ctField4.setModifiers(Modifier.STATIC);
      ctField4.setModifiers(Modifier.FINAL);
      ctClass.addField(ctField4, "7172649826282703560L");
      ctClass.setInterfaces(new CtClass[]{classPool.get("java.io.Serializable")});
      ctClass.addMethod(CtNewMethod.setter("setValue", ctField1));
      ctClass.addMethod(CtNewMethod.setter("setGmtCreate", ctField2));
      ctClass.addMethod(CtNewMethod.setter("setExpireTime", ctField3));
      ctClass.addMethod(CtNewMethod.getter("getValue", ctField1));
      ctClass.addMethod(CtNewMethod.getter("getGmtCreate", ctField2));
      ctClass.addMethod(CtNewMethod.getter("getExpireTime", ctField3));
      CtConstructor constructor = new CtConstructor(new CtClass[]{classPool.get("java.lang.Object"), classPool.get("long"), classPool.get("int")}, ctClass);
      constructor.setBody("{$0.value = $1;$0.gmtCreate = $2;$0.expireTime = $3;}");
      ctClass.addConstructor(constructor);
      ctClass.writeFile("/dd/aa");
      Object o = ctClass.toClass().newInstance();
      ctClass.detach();
      return (T) o;
   }

   /**
    * 序列化 克隆處理
    * @param obj
    * @return CacheEntity
    */
   private <T extends Serializable> T clone(T obj){
      T target = null;
//      try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos);
//           ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))){
//         oos.writeObject(obj);
//         target = (CacheEntity) ois.readObject();
//      }catch (Exception e){
//         logger.error("CLONE VALUE HAS THROWS AN EXCEPTION:", e);
//      }
//      return target;
      try {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(obj);
         oos.close();
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
         ObjectInputStream ois = new ObjectInputStream(bais);
         target = (T) ois.readObject();
         ois.close();
      }catch (Exception e){
         logger.error("CLONE VALUE HAS THROWS AN EXCEPTION:", e);
      }
      return target;
   }

   /**
   *  清除緩存
   */
   public void clear(){
      cache.clear();
   }

   /**
    * 根據key得到value
    * @param key
    * @return Object
    */
   public Object getValue(String key){
      return cache.get(key).getObj();
   }
}

 

/**
 * @author : hilite
 * @version : 1.0
 * @Title : CacheEntity
 * @Description :
 * @date : 2019/9/18
 * @CopyRight : Copyright (c) 2015-2019 All Rights Reserved.
 * @Compony : hilite.cn Inc.
 */
public class CacheEntity implements Serializable {

   private static final long serialVersionUID = 7172649826282703560L;

   private Object obj;

   private long gmtCreate;

   private int expireTime;

   public CacheEntity(Object obj, long gmtCreate, int expireTime) {
      this.obj = obj;
      this.gmtCreate = gmtCreate;
      this.expireTime = expireTime;
   }

   public Object getObj() {
      return obj;
   }

   public void setObj(Object obj) {
      this.obj = obj;
   }

   public long getGmtCreate() {
      return gmtCreate;
   }

   public void setGmtCreate(long gmtCreate) {
      this.gmtCreate = gmtCreate;
   }

   public int getExpireTime() {
      return expireTime;
   }

   public void setExpireTime(int expireTime) {
      this.expireTime = expireTime;
   }
}

   參考了網上的資料,自己寫了個實現本地緩存的類,clone方法只是一個保險的方法,防止出現值傳遞的問題,會出現這個問題的原因是因爲在外部存儲對象(不包括String及包裝類型)做緩存時,如果再存儲過後,對對象進行修改的話,也會對緩存中的對象進行修改,關於java值傳遞的問題可自行百度,這裏不做贅述。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章