java cache过期策略两种实现,一个基于list轮询一个基于timer定时

时间:2022-11-14 23:27:44

       最近项目要引入缓存机制,但是不想引入分布式的缓存框架,所以自己就写了一个轻量级的缓存实现,有两个版本,一个是通过timer实现其超时过期处理,另外一个是通过list轮询。
       首先要了解下java1.6中的ConcurrentMap ,他是一个线程安全的Map实现,特别说明的是在没有特别需求的情况下可以用ConcurrentHashMap。我是想学习一下读写锁的应用,就自己实现了一个SimpleConcurrentHashMap.

[java] view plain copy  print?
  1. package com.cttc.cache.entity;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.HashSet;  
  6. import java.util.Map;  
  7. import java.util.Set;  
  8. import java.util.concurrent.locks.Lock;  
  9. import java.util.concurrent.locks.ReadWriteLock;  
  10. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  11.   
  12. public class SimpleConcurrentMap<K, V> implements Map<K, V> {  
  13.     final ReadWriteLock lock = new ReentrantReadWriteLock();  
  14.     final Lock r = lock.readLock();  
  15.     final Lock w = lock.writeLock();  
  16.     final Map<K, V> map;  
  17.       
  18.     public SimpleConcurrentMap(Map<K, V> map) {  
  19.         this.map = map;  
  20.         if (map == null) throw new NullPointerException();  
  21.     }  
  22.   
  23.     public void clear() {  
  24.         w.lock();  
  25.         try {  
  26.             map.clear();  
  27.         } finally {  
  28.             w.unlock();  
  29.         }  
  30.     }  
  31.   
  32.     public boolean containsKey(Object key) {  
  33.         r.lock();  
  34.         try {  
  35.             return map.containsKey(key);  
  36.         } finally {  
  37.             r.unlock();  
  38.         }  
  39.     }  
  40.   
  41.     public boolean containsValue(Object value) {  
  42.         r.lock();  
  43.         try {  
  44.             return map.containsValue(value);  
  45.         } finally {  
  46.             r.unlock();  
  47.         }  
  48.     }  
  49.   
  50.     public Set<java.util.Map.Entry<K, V>> entrySet() {  
  51.         throw new UnsupportedOperationException();  
  52.     }  
  53.   
  54.     public V get(Object key) {  
  55.         r.lock();  
  56.         try {  
  57.             return map.get(key);  
  58.         } finally {  
  59.             r.unlock();  
  60.         }  
  61.     }  
  62.   
  63.     public boolean isEmpty() {  
  64.         r.lock();  
  65.         try {  
  66.             return map.isEmpty();  
  67.         } finally {  
  68.             r.unlock();  
  69.         }  
  70.     }  
  71.   
  72.     public Set<K> keySet() {  
  73.         r.lock();  
  74.         try {  
  75.             return new HashSet<K>(map.keySet());  
  76.         } finally {  
  77.             r.unlock();  
  78.         }  
  79.     }  
  80.   
  81.     public V put(K key, V value) {  
  82.         w.lock();  
  83.         try {  
  84.             return map.put(key, value);  
  85.         } finally {  
  86.             w.unlock();  
  87.         }  
  88.     }  
  89.   
  90.     public void putAll(Map<? extends K, ? extends V> m) {  
  91.         w.lock();  
  92.         try {  
  93.             map.putAll(m);  
  94.         } finally {  
  95.             w.unlock();  
  96.         }  
  97.     }  
  98.   
  99.     public V remove(Object key) {  
  100.         w.lock();  
  101.         try {  
  102.             return map.remove(key);  
  103.         } finally {  
  104.             w.unlock();  
  105.         }  
  106.     }  
  107.   
  108.     public int size() {  
  109.         r.lock();  
  110.         try {  
  111.             return map.size();  
  112.         } finally {  
  113.             r.unlock();  
  114.         }  
  115.     }  
  116.   
  117.     public Collection<V> values() {  
  118.         r.lock();  
  119.         try {  
  120.             return new ArrayList<V>(map.values());  
  121.         } finally {  
  122.             r.unlock();  
  123.         }  
  124.     }  
  125.   
  126. }  


缓存对象CacheEntity.Java为:

[html] view plain copy  print?
  1. package com.cttc.cache.entity;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class CacheEntity implements Serializable{  
  6.     private static final long serialVersionUID = -3971709196436977492L;  
  7.     private final int DEFUALT_VALIDITY_TIME = 20;//默认过期时间 20秒  
  8.       
  9.     private String cacheKey;  
  10.     private Object cacheContext;  
  11.     private int validityTime;//有效期时长,单位:秒  
  12.     private long timeoutStamp;//过期时间戳  
  13.       
  14.     private CacheEntity(){  
  15.         this.timeoutStamp = System.currentTimeMillis() + DEFUALT_VALIDITY_TIME * 1000;  
  16.         this.validityTime = DEFUALT_VALIDITY_TIME;  
  17.     }  
  18.       
  19.     public CacheEntity(String cacheKey, Object cacheContext){  
  20.         this();  
  21.         this.cacheKey = cacheKey;  
  22.         this.cacheContext = cacheContext;  
  23.     }  
  24.       
  25.     public CacheEntity(String cacheKey, Object cacheContext, long timeoutStamp){  
  26.         this(cacheKey, cacheContext);  
  27.         this.timeoutStamp = timeoutStamp;  
  28.     }  
  29.       
  30.     public CacheEntity(String cacheKey, Object cacheContext, int validityTime){  
  31.         this(cacheKey, cacheContext);  
  32.         this.validityTime = validityTime;  
  33.         this.timeoutStamp = System.currentTimeMillis() + validityTime * 1000;  
  34.     }  
  35.   
  36.     public String getCacheKey() {  
  37.         return cacheKey;  
  38.     }  
  39.     public void setCacheKey(String cacheKey) {  
  40.         this.cacheKey = cacheKey;  
  41.     }  
  42.     public Object getCacheContext() {  
  43.         return cacheContext;  
  44.     }  
  45.     public void setCacheContext(Object cacheContext) {  
  46.         this.cacheContext = cacheContext;  
  47.     }  
  48.     public long getTimeoutStamp() {  
  49.         return timeoutStamp;  
  50.     }  
  51.     public void setTimeoutStamp(long timeoutStamp) {  
  52.         this.timeoutStamp = timeoutStamp;  
  53.     }  
  54.     public int getValidityTime() {  
  55.         return validityTime;  
  56.     }  
  57.     public void setValidityTime(int validityTime) {  
  58.         this.validityTime = validityTime;  
  59.     }  
  60. }  


List缓存处理对象:

[java] view plain copy  print?
  1. package com.cttc.cache.handler;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. import java.util.List;  
  6.   
  7. import com.cttc.cache.entity.CacheEntity;  
  8. import com.cttc.cache.entity.SimpleConcurrentMap;  
  9.   
  10. /** 
  11.  * @projName:WZServer 
  12.  * @className:CacheHandler 
  13.  * @description:缓存操作类,对缓存进行管理,采用处理队列,定时循环清理的方式 
  14.  * @creater:Administrator  
  15.  * @creatTime:2013年7月22日 上午9:18:54  
  16.  * @alter:Administrator 
  17.  * @alterTime:2013年7月22日 上午9:18:54   
  18.  * @remark: 
  19.  * @version  
  20.  */  
  21. public class CacheListHandler {  
  22.     private static final long SECOND_TIME = 1000;  
  23.     private static final SimpleConcurrentMap<String, CacheEntity> map;  
  24.     private static final List<CacheEntity> tempList;  
  25.       
  26.     static{  
  27.         tempList = new ArrayList<CacheEntity>();  
  28.         map = new SimpleConcurrentMap<String, CacheEntity>(new HashMap<String, CacheEntity>(1<<18));  
  29.         new Thread(new TimeoutTimerThread()).start();  
  30.     }  
  31.               
  32.     /** 
  33.      * 增加缓存对象 
  34.      * @param key 
  35.      * @param ce 
  36.      */  
  37.     public static void addCache(String key, CacheEntity ce){  
  38.         addCache(key, ce, ce.getValidityTime());  
  39.     }  
  40.       
  41.     /** 
  42.      * 增加缓存对象 
  43.      * @param key 
  44.      * @param ce 
  45.      * @param validityTime 有效时间 
  46.      */  
  47.     public static synchronized void addCache(String key, CacheEntity ce, int validityTime){  
  48.         ce.setTimeoutStamp(System.currentTimeMillis() + validityTime * SECOND_TIME);  
  49.         map.put(key, ce);  
  50.         //添加到过期处理队列  
  51.         tempList.add(ce);  
  52.     }  
  53.       
  54.     /** 
  55.      * 获取缓存对象 
  56.      * @param key 
  57.      * @return 
  58.      */  
  59.     public static synchronized CacheEntity getCache(String key){  
  60.         return map.get(key);  
  61.     }  
  62.       
  63.     /** 
  64.      * 检查是否含有制定key的缓冲 
  65.      * @param key 
  66.      * @return 
  67.      */  
  68.     public static synchronized boolean isConcurrent(String key){  
  69.         return map.containsKey(key);  
  70.     }  
  71.       
  72.     /** 
  73.      * 删除缓存 
  74.      * @param key 
  75.      */  
  76.     public static synchronized void removeCache(String key){  
  77.         map.remove(key);  
  78.     }  
  79.       
  80.     /** 
  81.      * 获取缓存大小 
  82.      * @param key 
  83.      */  
  84.     public static int getCacheSize(){  
  85.         return map.size();  
  86.     }  
  87.       
  88.     /** 
  89.      * 清除全部缓存 
  90.      */  
  91.     public static synchronized void clearCache(){  
  92.         tempList.clear();  
  93.         map.clear();  
  94.         System.out.println("clear cache");  
  95.     }  
  96.       
  97.     static class TimeoutTimerThread implements Runnable {  
  98.         public void run(){  
  99.             while(true){  
  100.                 try {  
  101.                     checkTime();  
  102.                 } catch (Exception e) {  
  103.                     e.printStackTrace();  
  104.                 }  
  105.             }  
  106.         }  
  107.           
  108.         /** 
  109.          * 过期缓存的具体处理方法 
  110.          * @throws Exception 
  111.          */  
  112.         private void checkTime() throws Exception{    
  113.             //"开始处理过期 ";  
  114.             CacheEntity tce = null;  
  115.             long timoutTime = 1000L;  
  116.               
  117.             //" 过期队列大小 : "+tempList.size());  
  118.             if(1 > tempList.size()){  
  119.                 System.out.println("过期队列空,开始轮询");  
  120.                 timoutTime = 1000L;  
  121.                 Thread.sleep(timoutTime);  
  122.                 return;  
  123.             }  
  124.               
  125.             tce = tempList.get(0);  
  126.             timoutTime = tce.getTimeoutStamp() - System.currentTimeMillis();  
  127.             //" 过期时间 : "+timoutTime);  
  128.             if(0 < timoutTime){  
  129.                 //设定过期时间  
  130.                 Thread.sleep(timoutTime);  
  131.                 return;  
  132.             }  
  133.             System.out.print(" 清除过期缓存 : "+tce.getCacheKey());  
  134.             //清除过期缓存和删除对应的缓存队列  
  135.             tempList.remove(tce);  
  136.             removeCache(tce.getCacheKey());  
  137.         }  
  138.     }  
  139. }  


Timer方式

[java] view plain copy  print?
  1. package com.cttc.cache.handler;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Timer;  
  5. import java.util.TimerTask;  
  6.   
  7. import com.cttc.cache.entity.CacheEntity;  
  8. import com.cttc.cache.entity.SimpleConcurrentMap;  
  9.   
  10. /** 
  11.  * @projName:WZServer 
  12.  * @className:CacheHandler 
  13.  * @description:缓存操作类,对缓存进行管理,清除方式采用Timer定时的方式 
  14.  * @creater:Administrator  
  15.  * @creatTime:2013年7月22日 上午9:18:54  
  16.  * @alter:Administrator 
  17.  * @alterTime:2013年7月22日 上午9:18:54   
  18.  * @remark: 
  19.  * @version  
  20.  */  
  21. public class CacheTimerHandler {  
  22.     private static final long SECOND_TIME = 1000;//默认过期时间 20秒  
  23.     private static final int DEFUALT_VALIDITY_TIME = 20;//默认过期时间 20秒  
  24.     private static final Timer timer ;  
  25.     private static final SimpleConcurrentMap<String, CacheEntity> map;  
  26.       
  27.     static{  
  28.         timer = new Timer();  
  29.         map = new SimpleConcurrentMap<String, CacheEntity>(new HashMap<String, CacheEntity>(1<<18));  
  30.     }  
  31.               
  32.     /** 
  33.      * 增加缓存对象 
  34.      * @param key 
  35.      * @param ce 
  36.      */  
  37.     public static void addCache(String key, CacheEntity ce){  
  38.         addCache(key, ce, DEFUALT_VALIDITY_TIME);  
  39.     }  
  40.       
  41.     /** 
  42.      * 增加缓存对象 
  43.      * @param key 
  44.      * @param ce 
  45.      * @param validityTime 有效时间 
  46.      */  
  47.     public static synchronized void addCache(String key, CacheEntity ce, int validityTime){  
  48.         map.put(key, ce);  
  49.         //添加过期定时  
  50.         timer.schedule(new TimeoutTimerTask(key), validityTime * SECOND_TIME);  
  51.     }  
  52.       
  53.     /** 
  54.      * 获取缓存对象 
  55.      * @param key 
  56.      * @return 
  57.      */  
  58.     public static synchronized CacheEntity getCache(String key){  
  59.         return map.get(key);  
  60.     }  
  61.       
  62.     /** 
  63.      * 检查是否含有制定key的缓冲 
  64.      * @param key 
  65.      * @return 
  66.      */  
  67.     public static synchronized boolean isConcurrent(String key){  
  68.         return map.containsKey(key);  
  69.     }  
  70.       
  71.     /** 
  72.      * 删除缓存 
  73.      * @param key 
  74.      */  
  75.     public static synchronized void removeCache(String key){  
  76.         map.remove(key);  
  77.     }  
  78.       
  79.     /** 
  80.      * 获取缓存大小 
  81.      * @param key 
  82.      */  
  83.     public static int getCacheSize(){  
  84.         return map.size();  
  85.     }  
  86.       
  87.     /** 
  88.      * 清除全部缓存 
  89.      */  
  90.     public static synchronized void clearCache(){  
  91.         if(null != timer){  
  92.             timer.cancel();  
  93.         }  
  94.         map.clear();  
  95.         System.out.println("clear cache");  
  96.     }  
  97.       
  98.     /** 
  99.      * @projName:WZServer 
  100.      * @className:TimeoutTimerTask 
  101.      * @description:清除超时缓存定时服务类 
  102.      * @creater:Administrator  
  103.      * @creatTime:2013年7月22日 上午9:34:39  
  104.      * @alter:Administrator 
  105.      * @alterTime:2013年7月22日 上午9:34:39   
  106.      * @remark: 
  107.      * @version  
  108.      */  
  109.     static class TimeoutTimerTask extends TimerTask{  
  110.         private String ceKey ;  
  111.           
  112.         public TimeoutTimerTask(String key){  
  113.             this.ceKey = key;  
  114.         }  
  115.   
  116.         @Override  
  117.         public void run() {  
  118.             CacheTimerHandler.removeCache(ceKey);  
  119.             System.out.println("remove : "+ceKey);  
  120.         }  
  121.     }  
  122. }  


timer方式有点是适用性更强,因为每个缓存的过期时间都可以独立配置的;ist只能适用于缓存时间都一样的线性过期。从性能开销方面,因为timer是与缓存对象数量成正比的,在缓存量很大的时候,在缓存时间内系统开销也随之提高;而list方式只要一个线程管理过期清理就可以了。



这里要感谢饭饭泛,从其微博学习到很多http://www.blogjava.net/xylz/archive/2010/07/14/326080.html

 

对timer方式的改进,定时程序只需要一个就可以了,过期时间,通过一个对象保存,根据每个对象的过期时间判断是否移除该缓存。于是得到下面的版本:

 

package com.cttc.cache.handler;

import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
/**
* 在缓存的时候,同时记录下该key,缓存时间,失效周期
* 在读取缓存的时候,更新该key的缓存时间,
* 定时器每两个小时运行一次,检查每个key是否过期,如果过期,删除Jboss中的cache
*
*/
public class CacheTimerMamager{
private static final long SECOND_TIME = 1000;//毫秒
private static final long DEFUALT_VALIDITY_TIME = SECOND_TIME * 60 * 60 * 2;//默认过期时间 :2小时
private static final Timer timer ;
private static final Map<String, CacheOutTime> map;

static{
timer = new Timer();
map = new HashMap<String, CacheOutTime>();
timer.schedule(new CacheTimerTask(), DEFUALT_VALIDITY_TIME, DEFUALT_VALIDITY_TIME);
}

/**
* 增加缓存对象
* @param key
* @param ce
*/
public static synchronized void addCache(String key){
CacheOutTime cot = map.get(key);
long outTime = System.currentTimeMillis()+DEFUALT_VALIDITY_TIME;
if(cot==null){
cot = new CacheOutTime(key, outTime);
map.put(key, cot);
}else{
//更新该key的过期时间
cot.setTimeoutStamp(outTime);
}
}

//移除cache
/**
* 考虑,在多线程时,当有线程已经取得缓存对象时,删掉了缓存,会产生什么情况
*/
public static synchronized void removeCache() {
CacheOutTime cot;
long currentTime = System.currentTimeMillis();
for (String key : map.keySet()) {
cot = map.get(key);
if(cot.getTimeoutStamp()<=currentTime){
System.out.println("remove : "+key);
}
}
}
static class CacheTimerTask extends TimerTask{
@Override
public void run() {
//移除cache
CacheTimerMamager.removeCache();
}
}

}

class CacheOutTime {
private String cacheKey;
private long timeoutStamp;//过期时间戳,在最后一次访问该key的时候计算得到

public CacheOutTime() {
super();
}
public CacheOutTime(String cacheKey, long timeoutStamp) {
super();
this.cacheKey = cacheKey;
this.timeoutStamp = timeoutStamp;
}

public String getCacheKey() {
return cacheKey;
}
public void setCacheKey(String cacheKey) {
this.cacheKey = cacheKey;
}
public long getTimeoutStamp() {
return timeoutStamp;
}
public void setTimeoutStamp(long timeoutStamp) {
this.timeoutStamp = timeoutStamp;
}
}

  

package com.cttc.cache.handler;
import java.util.HashMap;import java.util.Map;import java.util.Timer;import java.util.TimerTask;/** * 在缓存的时候,同时记录下该key,缓存时间,失效周期 * 在读取缓存的时候,更新该key的缓存时间, * 定时器每两个小时运行一次,检查每个key是否过期,如果过期,删除Jboss中的cache * */public class CacheTimerMamager{private static final long SECOND_TIME = 1000;//毫秒private static final long DEFUALT_VALIDITY_TIME = SECOND_TIME * 60 * 60 * 2;//默认过期时间 :2小时private static final Timer timer ;private static final Map<String, CacheOutTime> map;static{timer = new Timer();map = new HashMap<String, CacheOutTime>();timer.schedule(new CacheTimerTask(), DEFUALT_VALIDITY_TIME, DEFUALT_VALIDITY_TIME);}/** * 增加缓存对象 * @param key * @param ce */public static synchronized void  addCache(String key){CacheOutTime cot = map.get(key);long outTime = System.currentTimeMillis()+DEFUALT_VALIDITY_TIME;if(cot==null){cot = new CacheOutTime(key, outTime);map.put(key, cot);}else{//更新该key的过期时间cot.setTimeoutStamp(outTime);}}//移除cache/** * 考虑,在多线程时,当有线程已经取得缓存对象时,删掉了缓存,会产生什么情况 */public static synchronized void removeCache() {CacheOutTime cot;long currentTime = System.currentTimeMillis();for (String key : map.keySet()) {cot = map.get(key);if(cot.getTimeoutStamp()<=currentTime){System.out.println("remove : "+key);}}}static class CacheTimerTask extends TimerTask{@Overridepublic void run() {//移除cacheCacheTimerMamager.removeCache();}}}
class CacheOutTime {private String cacheKey;private long timeoutStamp;//过期时间戳,在最后一次访问该key的时候计算得到public CacheOutTime() {super();}public CacheOutTime(String cacheKey, long timeoutStamp) {super();this.cacheKey = cacheKey;this.timeoutStamp = timeoutStamp;}public String getCacheKey() {return cacheKey;}public void setCacheKey(String cacheKey) {this.cacheKey = cacheKey;}public long getTimeoutStamp() {return timeoutStamp;}public void setTimeoutStamp(long timeoutStamp) {this.timeoutStamp = timeoutStamp;}}