简单的java缓存类实现

来源:互联网 发布:java public class读法 编辑:程序博客网 时间:2024/05/17 01:50

提到缓存,首先要说的就是缓存算法(淘汰算法),常见算法有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队列头部的数据;

首先定义缓存接口类,代码如下:

/** * 缓存接口 * @author Robin */public interface Cache<K,V> {    /**     * 返回当前缓存的大小     *      * @return       */    int size();    /**     * 返回默认存活时间     *      * @return     */    long getDefaultExpire();    /**     * 向缓存添加value对象,其在缓存中生存时间为默认值     *      * @param key     * @param value     */    void put(K key ,V value) ;    /**     * 向缓存添加value对象,并指定存活时间     * @param key     * @param value     * @param expire  过期时间     */    void put(K key ,V value , long expire ) ;    /**     * 查找缓存对象     * @param key     * @return     */    V get(K key);    /**     * 淘汰对象     *      * @return  被删除对象大小     */    int eliminate();    /**     * 缓存是否已经满     * @return     */    boolean isFull();    /**     * 删除缓存对象     *      * @param key     */    void remove(K key);    /**     * 清除所有缓存对象     */    void clear();    /**     * 返回缓存大小     *      * @return       */    int getCacheSize();    /**     * 缓存中是否为空     */    boolean isEmpty();}

然后实现这个接口,代码如下:

import java.util.Map;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 默认实现 */public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {    class CacheObject<K2,V2> {        CacheObject(K2 key, V2 value, long ttl) {            this.key = key;            this.cachedObject = value;            this.ttl = ttl;            this.lastAccess = System.currentTimeMillis();        }        final K2 key;        final V2 cachedObject;        long lastAccess;        // 最后访问时间        long accessCount;       // 访问次数        long ttl;               // 对象存活时间(time-to-live)        boolean isExpired() {            if (ttl == 0) {                return false;            }            return lastAccess + ttl < System.currentTimeMillis();        }        V2 getObject() {            lastAccess = System.currentTimeMillis();            accessCount++;            return cachedObject;        }    }    protected Map<K,CacheObject<K,V>> cacheMap;    private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();    private final Lock readLock = cacheLock.readLock();    private final Lock writeLock = cacheLock.writeLock();    protected int cacheSize;      // 缓存大小 , 0 -> 无限制    protected  boolean existCustomExpire ; //是否设置默认过期时间    public int getCacheSize() {        return cacheSize;    }    protected long defaultExpire;     // 默认过期时间, 0 -> 永不过期    public AbstractCacheMap(int cacheSize ,long defaultExpire){        this.cacheSize  = cacheSize ;        this.defaultExpire  = defaultExpire ;    }    public long getDefaultExpire() {        return defaultExpire;    }    protected boolean isNeedClearExpiredObject(){        return defaultExpire > 0 || existCustomExpire ;    }    public void put(K key, V value) {        put(key, value, defaultExpire );    }    public void put(K key, V value, long expire) {        writeLock.lock();        try {            CacheObject<K,V> co = new CacheObject<K,V>(key, value, expire);            if (expire != 0) {                existCustomExpire = true;            }            if (isFull()) {                eliminate() ;            }            cacheMap.put(key, co);        }        finally {            writeLock.unlock();        }    }    /**     * {@inheritDoc}     */    public V get(K key) {        readLock.lock();        try {            CacheObject<K,V> co = cacheMap.get(key);            if (co == null) {                return null;            }            if (co.isExpired() == true) {                cacheMap.remove(key);                return null;            }            return co.getObject();        }        finally {            readLock.unlock();        }    }    public final int eliminate() {        writeLock.lock();        try {            return eliminateCache();        }        finally {            writeLock.unlock();        }    }    /**     * 淘汰对象具体实现     *      * @return     */    protected abstract int eliminateCache();     public boolean isFull() {        if (cacheSize == 0) {//o -> 无限制            return false;        }        return cacheMap.size() >= cacheSize;    }    public void remove(K key) {        writeLock.lock();        try {            cacheMap.remove(key);        }        finally {            writeLock.unlock();        }    }    public void clear() {        writeLock.lock();        try {            cacheMap.clear();        }        finally {            writeLock.unlock();        }    }    public int size() {        return cacheMap.size();    }    public boolean isEmpty() {        return size() == 0;    }}

这样基本实现了一个缓存类的构造,接下来是三种淘汰算法的构造。
LRU缓存实现类,代码如下:

import java.util.Iterator;import java.util.LinkedHashMap;import java.util.Map;/** * LRU  实现 * @author Robin * * @param <K> * @param <V> */public class LRUCache<K, V> extends AbstractCacheMap<K, V> {    public LRUCache(int cacheSize, long defaultExpire) {        super(cacheSize , defaultExpire) ;        //linkedHash已经实现LRU算法 是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置        this.cacheMap = new LinkedHashMap<K, CacheObject<K, V>>( cacheSize +1 , 1f,true ) {            @Override            protected boolean removeEldestEntry(                    Map.Entry<K, CacheObject<K, V>> eldest) {                return LRUCache.this.removeEldestEntry(eldest);            }        };    }    private boolean removeEldestEntry(Map.Entry<K, CacheObject<K, V>> eldest) {        if (cacheSize == 0)            return false;        return size() > cacheSize;    }    /**     * 只需要实现清除过期对象就可以了,linkedHashMap已经实现LRU     */    @Override    protected int eliminateCache() {        if(!isNeedClearExpiredObject()){ return 0 ;}        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();        int count  = 0 ;        while(iterator.hasNext()){            CacheObject<K, V> cacheObject = iterator.next();            if(cacheObject.isExpired() ){                iterator.remove();                 count++ ;            }        }        return count;    }}

LFU实现类,代码如下:

import java.util.HashMap;import java.util.Iterator;//LFU实现public class LFUCache<K,V> extends AbstractCacheMap<K, V> {    public LFUCache(int cacheSize, long defaultExpire) {        super(cacheSize, defaultExpire);        cacheMap = new HashMap<K, CacheObject<K,V>>(cacheSize+1) ;    }    /**     * 实现删除过期对象 和 删除访问次数最少的对象      *      */    @Override    protected int eliminateCache() {        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();        int count  = 0 ;        long minAccessCount = Long.MAX_VALUE  ;        while(iterator.hasNext()){            CacheObject<K, V> cacheObject = iterator.next();            if(cacheObject.isExpired() ){                iterator.remove();                 count++ ;                continue ;            }else{                minAccessCount  = Math.min(cacheObject.accessCount , minAccessCount)  ;            }        }        if(count > 0 ) return count ;        if(minAccessCount != Long.MAX_VALUE ){            iterator = cacheMap.values().iterator();            while(iterator.hasNext()){                CacheObject<K, V> cacheObject = iterator.next();                cacheObject.accessCount  -=  minAccessCount ;                if(cacheObject.accessCount <= 0 ){                    iterator.remove();                    count++ ;                }            }        }        return count;    }}

FIFO实现类,代码如下:

import java.util.Iterator;import java.util.LinkedHashMap;/** * FIFO实现 * @author Robin * * @param <K> * @param <V> */public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {    public FIFOCache(int cacheSize, long defaultExpire) {        super(cacheSize, defaultExpire);        cacheMap = new LinkedHashMap<K, CacheObject<K, V>>(cacheSize + 1);    }    @Override    protected int eliminateCache() {        int count = 0;        K firstKey = null;        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();        while (iterator.hasNext()) {            CacheObject<K, V> cacheObject = iterator.next();            if (cacheObject.isExpired()) {                iterator.remove();                count++;            } else {                if (firstKey == null)                    firstKey = cacheObject.key;            }        }        if (firstKey != null && isFull()) {//删除过期对象还是满,继续删除链表第一个            cacheMap.remove(firstKey);        }        return count;    }}
原创粉丝点击