简单的java缓存实现(LRU,LFU,FIFO)

7 篇文章 0 订阅

原帖地址:http://my.oschina.net/u/866190/blog/188712

提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有LRU、LFU和FIFO等算法,每种算法各有各的优势和缺点及适应环境。

1、LRU(Least Recently Used ,最近最少使用)
算法根据数据的最近访问记录来淘汰数据,其原理是如果数据最近被访问过,将来被访问的几概率相对比较高,最常见的实现是使用一个链表保存缓存数据,详细具体算法如下:
1. 新数据插入到链表头部;
2. 每当缓存数据命中,则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃;


2、LFU(Least Frequently Used,最不经常使用)
算法根据数据的历史访问频率来淘汰数据,其原理是如果数据过去被访问次数越多,将来被访问的几概率相对比较高。LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。
具体算法如下:
1. 新加入数据插入到队列尾部(因为引用计数为1);
2. 队列中的数据被访问后,引用计数增加,队列重新排序;
3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除;


3、FIFO(First In First Out ,先进先出)
算法是根据先进先出原理来淘汰数据的,实现上是最简单的一种,具体算法如下:
1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;
2. 淘汰FIFO队列头部的数据;


评价一个缓存算法好坏的标准主要有两个,一是命中率要高,二是算法要容易实现。当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。LFU效率要优于LRU,且能够避免周期性或者偶发性的操作导致缓存命中率下降的问题。但LFU需要记录数据的历史访问记录,一旦数据访问模式改变,LFU需要更长时间来适用新的访问模式,即:LFU存在历史数据影响将来数据的“缓存污染”效用。FIFO虽然实现很简单,但是命中率很低,实际上也很少使用这种算法。

基于现有jdk类库,我们可以很容易实现上面的缓存算法

首先定义缓存接口类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 缓存接口 
  3.  * @author Wen 
  4.  * 
  5.  */  
  6. public interface Cache<K,V> {  
  7.     /** 
  8.      * 返回当前缓存的大小 
  9.      *  
  10.      * @return   
  11.      */  
  12.     int size();  
  13.       
  14.     /** 
  15.      * 返回默认存活时间 
  16.      *  
  17.      * @return 
  18.      */  
  19.     long getDefaultExpire();  
  20.       
  21.     /** 
  22.      * 向缓存添加value对象,其在缓存中生存时间为默认值 
  23.      *  
  24.      * @param key 
  25.      * @param value 
  26.      */  
  27.     void put(K key ,V value) ;  
  28.       
  29.     /** 
  30.      * 向缓存添加value对象,并指定存活时间 
  31.      * @param key 
  32.      * @param value 
  33.      * @param expire  过期时间 
  34.      */  
  35.     void put(K key ,V value , long expire ) ;  
  36.       
  37.     /** 
  38.      * 查找缓存对象 
  39.      * @param key 
  40.      * @return 
  41.      */  
  42.     V get(K key);  
  43.       
  44.     /** 
  45.      * 淘汰对象 
  46.      *  
  47.      * @return  被删除对象大小 
  48.      */  
  49.     int eliminate();  
  50.       
  51.     /** 
  52.      * 缓存是否已经满 
  53.      * @return 
  54.      */  
  55.     boolean isFull();  
  56.   
  57.     /** 
  58.      * 删除缓存对象 
  59.      *  
  60.      * @param key 
  61.      */  
  62.     void remove(K key);  
  63.   
  64.     /** 
  65.      * 清除所有缓存对象 
  66.      */  
  67.     void clear();  
  68.   
  69.     /** 
  70.      * 返回缓存大小 
  71.      *  
  72.      * @return   
  73.      */  
  74.     int getCacheSize();  
  75.   
  76.     /** 
  77.      * 缓存中是否为空 
  78.      */  
  79.     boolean isEmpty();  
  80.   
  81. }  

基本实现抽象类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.util.Map;  
  2. import java.util.concurrent.locks.Lock;  
  3. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  4.   
  5. /** 
  6.  * 默认实现 
  7.  */  
  8. public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {  
  9.   
  10.     class CacheObject<K2,V2> {  
  11.         CacheObject(K2 key, V2 value, long ttl) {  
  12.             this.key = key;  
  13.             this.cachedObject = value;  
  14.             this.ttl = ttl;  
  15.             this.lastAccess = System.currentTimeMillis();  
  16.         }  
  17.   
  18.         final K2 key;  
  19.         final V2 cachedObject;  
  20.         long lastAccess;        // 最后访问时间  
  21.         long accessCount;       // 访问次数  
  22.         long ttl;               // 对象存活时间(time-to-live)  
  23.   
  24.         boolean isExpired() {  
  25.             if (ttl == 0) {  
  26.                 return false;  
  27.             }  
  28.             return lastAccess + ttl < System.currentTimeMillis();  
  29.         }  
  30.         V2 getObject() {  
  31.             lastAccess = System.currentTimeMillis();  
  32.             accessCount++;  
  33.             return cachedObject;  
  34.         }  
  35.     }  
  36.   
  37.     protected Map<K,CacheObject<K,V>> cacheMap;  
  38.   
  39.     private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();  
  40.     private final Lock readLock = cacheLock.readLock();  
  41.     private final Lock writeLock = cacheLock.writeLock();  
  42.   
  43.   
  44.   
  45.     protected int cacheSize;      // 缓存大小 , 0 -> 无限制  
  46.       
  47.     protected  boolean existCustomExpire ; //是否设置默认过期时间  
  48.       
  49.     public int getCacheSize() {  
  50.         return cacheSize;  
  51.     }  
  52.   
  53.     protected long defaultExpire;     // 默认过期时间, 0 -> 永不过期  
  54.       
  55.     public AbstractCacheMap(int cacheSize ,long defaultExpire){  
  56.         this.cacheSize  = cacheSize ;  
  57.         this.defaultExpire  = defaultExpire ;  
  58.     }  
  59.   
  60.       
  61.     public long getDefaultExpire() {  
  62.         return defaultExpire;  
  63.     }  
  64.   
  65.   
  66.     protected boolean isNeedClearExpiredObject(){  
  67.         return defaultExpire > 0 || existCustomExpire ;  
  68.     }  
  69.   
  70.       
  71.     public void put(K key, V value) {  
  72.         put(key, value, defaultExpire );  
  73.     }  
  74.   
  75.   
  76.     public void put(K key, V value, long expire) {  
  77.         writeLock.lock();  
  78.   
  79.         try {  
  80.             CacheObject<K,V> co = new CacheObject<K,V>(key, value, expire);  
  81.             if (expire != 0) {  
  82.                 existCustomExpire = true;  
  83.             }  
  84.             if (isFull()) {  
  85.                 eliminate() ;  
  86.             }  
  87.             cacheMap.put(key, co);  
  88.         }  
  89.         finally {  
  90.             writeLock.unlock();  
  91.         }  
  92.     }  
  93.   
  94.   
  95.   
  96.     /** 
  97.      * {@inheritDoc} 
  98.      */  
  99.     public V get(K key) {  
  100.         readLock.lock();  
  101.   
  102.         try {  
  103.             CacheObject<K,V> co = cacheMap.get(key);  
  104.             if (co == null) {  
  105.                 return null;  
  106.             }  
  107.             if (co.isExpired() == true) {  
  108.                 cacheMap.remove(key);  
  109.                 return null;  
  110.             }  
  111.   
  112.             return co.getObject();  
  113.         }  
  114.         finally {  
  115.             readLock.unlock();  
  116.         }  
  117.     }  
  118.       
  119.     public final int eliminate() {  
  120.         writeLock.lock();  
  121.         try {  
  122.             return eliminateCache();  
  123.         }  
  124.         finally {  
  125.             writeLock.unlock();  
  126.         }  
  127.     }  
  128.       
  129.     /** 
  130.      * 淘汰对象具体实现 
  131.      *  
  132.      * @return 
  133.      */  
  134.     protected abstract int eliminateCache();   
  135.   
  136.   
  137.       
  138.     public boolean isFull() {  
  139.         if (cacheSize == 0) {//o -> 无限制  
  140.             return false;  
  141.         }  
  142.         return cacheMap.size() >= cacheSize;  
  143.     }  
  144.   
  145.       
  146.     public void remove(K key) {  
  147.         writeLock.lock();  
  148.         try {  
  149.             cacheMap.remove(key);  
  150.         }  
  151.         finally {  
  152.             writeLock.unlock();  
  153.         }  
  154.     }  
  155.   
  156.       
  157.     public void clear() {  
  158.         writeLock.lock();  
  159.         try {  
  160.             cacheMap.clear();  
  161.         }  
  162.         finally {  
  163.             writeLock.unlock();  
  164.         }  
  165.     }  
  166.   
  167.     public int size() {  
  168.         return cacheMap.size();  
  169.     }  
  170.   
  171.       
  172.     public boolean isEmpty() {  
  173.         return size() == 0;  
  174.     }  
  175. }  

LRU缓存实现类

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.util.Iterator;  
  2. import java.util.LinkedHashMap;  
  3. import java.util.Map;  
  4.   
  5. /**  
  6.  * LRU  实现  
  7.  * @author Wen  
  8.  *  
  9.  * @param <K>  
  10.  * @param <V>  
  11.  */  
  12. public class LRUCache<K, V> extends AbstractCacheMap<K, V> {  
  13.   
  14.     public LRUCache(int cacheSize, long defaultExpire) {  
  15.           
  16.         super(cacheSize , defaultExpire) ;  
  17.   
  18.         //linkedHash已经实现LRU算法 是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置  
  19.         this.cacheMap = new LinkedHashMap<K, CacheObject<K, V>>( cacheSize +1 , 1f,true ) {  
  20.   
  21.             @Override  
  22.             protected boolean removeEldestEntry(  
  23.                     Map.Entry<K, CacheObject<K, V>> eldest) {  
  24.   
  25.                 return LRUCache.this.removeEldestEntry(eldest);  
  26.             }  
  27.   
  28.         };  
  29.     }  
  30.   
  31.     private boolean removeEldestEntry(Map.Entry<K, CacheObject<K, V>> eldest) {  
  32.   
  33.         if (cacheSize == 0)  
  34.             return false;  
  35.   
  36.         return size() > cacheSize;  
  37.     }  
  38.   
  39.     /**  
  40.      * 只需要实现清除过期对象就可以了,linkedHashMap已经实现LRU  
  41.      */  
  42.     @Override  
  43.     protected int eliminateCache() {  
  44.   
  45.         if(!isNeedClearExpiredObject()){ return 0 ;}  
  46.           
  47.         Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();  
  48.         int count  = 0 ;  
  49.         while(iterator.hasNext()){  
  50.             CacheObject<K, V> cacheObject = iterator.next();  
  51.               
  52.             if(cacheObject.isExpired() ){  
  53.                 iterator.remove();   
  54.                 count++ ;  
  55.             }  
  56.         }  
  57.           
  58.         return count;  
  59.     }  
  60.   
  61. }  

LFU实现类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.util.HashMap;  
  2. import java.util.Iterator;  
  3.   
  4. //LFU实现  
  5. public class LFUCache<K,V> extends AbstractCacheMap<K, V> {  
  6.       
  7.   
  8.     public LFUCache(int cacheSize, long defaultExpire) {  
  9.         super(cacheSize, defaultExpire);  
  10.         cacheMap = new HashMap<K, CacheObject<K,V>>(cacheSize+1) ;  
  11.     }  
  12.   
  13.     /** 
  14.      * 实现删除过期对象 和 删除访问次数最少的对象  
  15.      *  
  16.      */  
  17.     @Override  
  18.     protected int eliminateCache() {  
  19.         Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();  
  20.         int count  = 0 ;  
  21.         long minAccessCount = Long.MAX_VALUE  ;  
  22.         while(iterator.hasNext()){  
  23.             CacheObject<K, V> cacheObject = iterator.next();  
  24.               
  25.             if(cacheObject.isExpired() ){  
  26.                 iterator.remove();   
  27.                 count++ ;  
  28.                 continue ;  
  29.             }else{  
  30.                 minAccessCount  = Math.min(cacheObject.accessCount , minAccessCount)  ;  
  31.             }  
  32.         }  
  33.           
  34.         if(count > 0 ) return count ;  
  35.           
  36.         if(minAccessCount != Long.MAX_VALUE ){  
  37.               
  38.             iterator = cacheMap.values().iterator();  
  39.               
  40.             while(iterator.hasNext()){  
  41.                 CacheObject<K, V> cacheObject = iterator.next();  
  42.                   
  43.                 cacheObject.accessCount  -=  minAccessCount ;  
  44.                   
  45.                 if(cacheObject.accessCount <= 0 ){  
  46.                     iterator.remove();  
  47.                     count++ ;  
  48.                 }  
  49.                   
  50.             }  
  51.               
  52.         }  
  53.           
  54.         return count;  
  55.     }  
  56.   
  57. }  

FIFO实现类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. import java.util.Iterator;  
  2. import java.util.LinkedHashMap;  
  3. /** 
  4.  * FIFO实现 
  5.  * @author Wen 
  6.  * 
  7.  * @param <K> 
  8.  * @param <V> 
  9.  */  
  10. public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {  
  11.   
  12.     public FIFOCache(int cacheSize, long defaultExpire) {  
  13.         super(cacheSize, defaultExpire);  
  14.         cacheMap = new LinkedHashMap<K, CacheObject<K, V>>(cacheSize + 1);  
  15.     }  
  16.   
  17.     @Override  
  18.     protected int eliminateCache() {  
  19.   
  20.         int count = 0;  
  21.         K firstKey = null;  
  22.   
  23.         Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();  
  24.         while (iterator.hasNext()) {  
  25.             CacheObject<K, V> cacheObject = iterator.next();  
  26.   
  27.             if (cacheObject.isExpired()) {  
  28.                 iterator.remove();  
  29.                 count++;  
  30.             } else {  
  31.                 if (firstKey == null)  
  32.                     firstKey = cacheObject.key;  
  33.             }  
  34.         }  
  35.   
  36.         if (firstKey != null && isFull()) {//删除过期对象还是满,继续删除链表第一个  
  37.             cacheMap.remove(firstKey);  
  38.         }  
  39.   
  40.         return count;  
  41.     }  
  42.   
  43. }  
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值