今天和同事聊到了缓存,在Java中实现进程缓存。这里主要思想是,用一个map做缓存。缓存有个生存时间,过期就删除缓存。这里可以考虑两种删除策略,一种是起一个线程,定期删除过期的key。第二个是,剔除模式,比较懒,访问到某个key的时候才,才去检查这个key是否过期,过期删除。
首先,对要缓存的value做了层封装,带了个时间戳
/** * Created by gxf on 2017/6/28. */ public class ValueWithTimeStamp<V>{ private long expireTime; private V value; public ValueWithTimeStamp(long expireTime, V value) { this.expireTime = expireTime; this.value = value; } public long getExpireTime() { return expireTime; } public void setExpireTime(long expireTime) { this.expireTime = expireTime; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } }
ok,起线程定期删除策略的模式
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Created by gxf on 2017/6/28. * 使用线程,定期删除过期的key */ public class CacheMap<K, V> { //这里使用ConcurrentHashMap避免,clean的时候,主线程修改chache,造成异常, //使用ConcurrentHashMap可以控制并发修改cache private Map<K, ValueWithTimeStamp<V>> cache = new ConcurrentHashMap<K, ValueWithTimeStamp<V>>(); private static boolean cleanTaskIsRunning = false; //ttl 过期时间 单位:秒 public void put(K key, V value, int ttl){ long expireTime = System.currentTimeMillis() + ttl * 1000; ValueWithTimeStamp<V> valueWithTimeStamp = new ValueWithTimeStamp<>(expireTime, value); cache.put(key, valueWithTimeStamp); if(!cleanTaskIsRunning){ startCleanTask(); cleanTaskIsRunning = !cleanTaskIsRunning; } } public V get(K key){ ValueWithTimeStamp<V> valueWithTimeStamp = cache.get(key); return null == valueWithTimeStamp ? null : valueWithTimeStamp.getValue(); } /** * 启动清理线程 * */ private void startCleanTask(){ Thread cleanThread = new Thread(new CleanTask()); cleanThread.start(); } /** * 清理过期的key * */ class CleanTask implements Runnable{ public void run(){ while(true){ long currentTime = System.currentTimeMillis(); //遍历map for(Map.Entry<K, ValueWithTimeStamp<V>> entry : cache.entrySet()){ ValueWithTimeStamp<V> valueWithTimeStamp = entry.getValue(); long expireTime = valueWithTimeStamp.getExpireTime(); //过期时间到了 if(currentTime > expireTime){ System.out.println("key : " + entry.getKey() + " expired "); cache.remove(entry.getKey()); } //if } //for //每隔1s扫描map对象 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } //while } } }
注意,这里需要使用ConcurrentHashMap做缓存。不然会出现多线线程操作map对象的异常
第二种策略,剔除模式
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Created by 58 on 2017/6/28. * 这里使用懒惰模式,获取key的时候,才剔除过期的key */ public class CacheMap1<K, V> { private Map<K, ValueWithTimeStamp<V>> cache = new ConcurrentHashMap<K, ValueWithTimeStamp<V>>(); public void put(K key, V value, int ttl){ long expireTime = System.currentTimeMillis() + ttl * 1000; ValueWithTimeStamp<V> valueWithTimeStamp = new ValueWithTimeStamp<V>(expireTime, value); cache.put(key, valueWithTimeStamp); } public V get(K key){ ValueWithTimeStamp<V> valueWithTimeStamp = cache.get(key); long expireTime = valueWithTimeStamp.getExpireTime(); long currentTime = System.currentTimeMillis(); //key已经过期,删除,返回null if(currentTime > expireTime){ System.out.println("key :" + key + " is expired."); cache.remove(key); return null; } return cache.get(key).getValue(); } }
项目中用到了redis,用这种方式应该会更快,这两天优化一下代码