jodd.cache源码阅读笔记

2017-10-26  本文已影响0人  Chigusa

简介

jodd.cache包中有FIFOLRULFU等几种缓存置换算法的实现

FIFO -- 先进先出

FIFO
  1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动
  2. 淘汰FIFO队列头部的数据

LRU -- 最久未使用

LRU
  1. 新数据插入到链表头部
  2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部
  3. 当链表满的时候,将链表尾部的数据丢弃

LFU -- 最近最少使用

LFU
  1. 新加入数据插入到队列尾部(因为引用计数为1)
  2. 队列中的数据被访问后,引用计数增加,队列重新排序
  3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除

代码实现

继承关系

UML

Cache

public interface Cache<K, V> {

    /**
     * 返回缓存容器的大小限制,如果为0则为不限制
     */
    int limit();

    /**
     * 返回超时时间,如果为0则没有超时
     */
    long timeout();

    /**
     * 使用默认超时时间(0)添加缓存对象
     */
    void put(K key, V object);

    /**
     * 使用自定的超时时间添加缓存对象
     * 如果缓存容器已经满将调用purne()方法来获得空间
     */
    void put(K key, V object, long timeout);

    /**
     * 根据键从容器中取得缓存对象
     * 如果该对象已被清除将返回null
     */
    V get(K key);

    /**
     * 从缓存容器中移除对象并返回移除的对象的个数
     */
    int prune();

    /**
     * 返回缓存容器是否已满,当且仅当容器容积有限制的情况下
     */
    boolean isFull();

    /**
     * 从容器中移除一个对象
     */
    void remove(K key);

    /**
     * 清空容器
     */
    void clear();

    /**
     * 返回当前缓存的对象的数量
     */
    int size();

    /**
     * 返回缓存容器是否为空
     */
    boolean isEmpty();

    /**
     * 返回一个包含容器中缓存对象的Map对象
     * 期间会锁定容器
     */
    Map<K, V> snapshot();
}

AbstractCacheMap

public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
    // Cache Entry
    class CacheObject<K2, V2> {
        CacheObject(K2 key, V2 object, long ttl) {
            this.key = key;
            this.cachedObject = object;
            this.ttl = ttl;
            this.lastAccess = System.currentTimeMillis();
        }

        final K2 key;
        final V2 cachedObject;
        long lastAccess;        // 最后访问时间,供LRU实现使用
        long accessCount;        // 访问计数,供LFU实现使用
        long ttl;                // 对象超时时间, 0 = 没有超时

        // 是否过期
        boolean isExpired() {
            if (ttl == 0) {
                return false;
            }
            return lastAccess + ttl < System.currentTimeMillis();
        }

        // 获得缓存对象并刷新访问时间
        V2 getObject() {
            lastAccess = System.currentTimeMillis();
            accessCount++;
            return cachedObject;
        }
    }

    // 用于存放缓存的Map,在实现类中具体实例化
    protected Map<K, CacheObject<K, V>> cacheMap;
    // 读写锁
    private final StampedLock lock = new StampedLock();

    // ---------------------------------------------------------------- properties
    // 缓存大小
    protected int cacheSize;      // max cache size, 0 = no limit

    public int limit() {
        return cacheSize;
    }

    // 缓存容器默认超时时间,默认0
    protected long timeout;     // default timeout, 0 = no timeout

    /**
     * Returns default cache timeout or <code>0</code> if it is not set.
     * Timeout can be set individually for each object.
     */
    public long timeout() {
        return timeout;
    }

    // 是否有缓存对象曾自定义超时时间
    protected boolean existCustomTimeout;

    // 缓存替换时是否需要对对象的存活状态进行判断
    protected boolean isPruneExpiredActive() {
        return (timeout != 0) || existCustomTimeout;
    }


    // ---------------------------------------------------------------- put


    public void put(K key, V object) {
        put(key, object, timeout);
    }


    public void put(K key, V object, long timeout) {
        final long stamp = lock.writeLock();

        try {
            CacheObject<K, V> co = new CacheObject<>(key, object, timeout);
            // 缓存对象自定义过超时时间
            if (timeout != 0) {
                existCustomTimeout = true;
            }
            // 判断是否达到缓存容器上限,是的话触发替换(达到最大容量,但键值已存在不触发,替换为新对象)
            if (isReallyFull(key)) {
                pruneCache();
            }
            cacheMap.put(key, co);
        } finally {
            lock.unlockWrite(stamp);
        }
    }


    // ---------------------------------------------------------------- get

    // 命中次数
    protected int hitCount;
    // 非命中次数
    protected int missCount;

    /**
     * Returns hit count.
     */
    public int getHitCount() {
        return hitCount;
    }

    /**
     * Returns miss count.
     */
    public int getMissCount() {
        return missCount;
    }

    public V get(K key) {
        long stamp = lock.readLock();

        try {
            CacheObject<K, V> co = cacheMap.get(key);
            if (co == null) {
                missCount++;
                return null;
            }
            // 判断是否过期
            if (co.isExpired()) {
                // 尝试获得写锁,获取失败则释放读锁,手动获得读锁
                final long newStamp = lock.tryConvertToWriteLock(stamp);

                if (newStamp != 0L) {
                    stamp = newStamp;
                    // lock is upgraded to write lock
                } else {
                    // manually upgrade lock to write lock
                    lock.unlockRead(stamp);
                    stamp = lock.writeLock();
                }
                // 移除过期对象
                CacheObject<K, V> removedCo = cacheMap.remove(key);
                // 触发移除后的钩子函数(Files Cache用的)
                if (removedCo != null) {
                    onRemove(removedCo.key, removedCo.cachedObject);
                }

                missCount++;
                return null;
            }
            // 缓存命中,返回对象
            hitCount++;
            return co.getObject();
        } finally {
            lock.unlock(stamp);
        }
    }

    // ---------------------------------------------------------------- prune

    // 缓存修剪具体实现
    protected abstract int pruneCache();

    public final int prune() {
        final long stamp = lock.writeLock();
        try {
            return pruneCache();
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    // ---------------------------------------------------------------- common
    // 以下方法基本为加锁访问Map的对应方法
    public boolean isFull() {
        if (cacheSize == 0) {
            return false;
        }
        return cacheMap.size() >= cacheSize;
    }

    protected boolean isReallyFull(K key) {
        if (cacheSize == 0) {
            return false;
        }
        if (cacheMap.size() >= cacheSize) {
            return !cacheMap.containsKey(key);
        } else {
            return false;
        }
    }

    public void remove(K key) {
        final long stamp = lock.writeLock();
        try {
            CacheObject<K, V> co = cacheMap.remove(key);
            if (co != null) {
                onRemove(co.key, co.cachedObject);
            }
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public void clear() {
        final long stamp = lock.writeLock();
        try {
            cacheMap.clear();
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public int size() {
        return cacheMap.size();
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    // 返回一个cache map的拷贝
    public Map<K, V> snapshot() {
        final long stamp = lock.writeLock();
        try {
            Map<K, V> map = new HashMap<>(cacheMap.size());
            cacheMap.forEach((key, cacheValue) -> map.put(key, cacheValue.getObject()));
            return map;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    // ---------------------------------------------------------------- protected

    /**
     * Callback called on item removal. The cache is still locked.
     */
    protected void onRemove(K key, V cachedObject) {
    }

}

LRUCache

public class LRUCache<K, V> extends AbstractCacheMap<K, V> {

    public LRUCache(int cacheSize) {
        this(cacheSize, 0);
    }

    /**
     * Creates a new LRU cache.
     */
    public LRUCache(int cacheSize, long timeout) {
        this.cacheSize = cacheSize;
        this.timeout = timeout;
        // cacheMap使用LinkedHashMap实现
        // 不自动扩容,按访问顺序排序,达到容量后移除末尾元素
        cacheMap = new LinkedHashMap<K, CacheObject<K, V>>(cacheSize + 1, 1.0f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                return LRUCache.this.removeEldestEntry(size());
            }
        };
    }

    /**
     * 用来判断是否需要移除尾部元素
     */
    protected boolean removeEldestEntry(int currentSize) {
        if (cacheSize == 0) {
            return false;
        }
        return currentSize > cacheSize;
    }

    // ---------------------------------------------------------------- prune

    // 遍历缓存map,移除超时对象,返回超时对象计数
    // 如果没有定义超时,直接返回0
    @Override
    protected int pruneCache() {
        if (!isPruneExpiredActive()) {
            return 0;
        }
        int count = 0;
        Iterator<CacheObject<K, V>> values = cacheMap.values().iterator();
        while (values.hasNext()) {
            CacheObject<K, V> co = values.next();
            if (co.isExpired()) {
                values.remove();
                onRemove(co.key, co.cachedObject);
                count++;
            }
        }
        return count;
    }
}

LFUCache

public class LFUCache<K, V> extends AbstractCacheMap<K, V> {

    public LFUCache(int maxSize) {
        this(maxSize, 0);
    }

    public LFUCache(int maxSize, long timeout) {
        this.cacheSize = maxSize;
        this.timeout = timeout;
        cacheMap = new HashMap<>(maxSize + 1);
    }

    // ---------------------------------------------------------------- prune

    /**
     * Prunes expired and, if cache is still full, the LFU element(s) from the cache.
     * On LFU removal, access count is normalized to value which had removed object.
     * Returns the number of removed objects.
     */
    @Override
    protected int pruneCache() {
        int count = 0;
        CacheObject<K, V> comin = null;

        // remove expired items and find cached object with minimal access count
        Iterator<CacheObject<K, V>> values = cacheMap.values().iterator();
        // 移除超时对象,并获得存活对象中访问计数中最小的对象==>comin
        while (values.hasNext()) {
            CacheObject<K, V> co = values.next();
            if (co.isExpired()) {
                values.remove();
                onRemove(co.key, co.cachedObject);
                count++;
                continue;
            }

            if (comin == null) {
                comin = co;
            } else {
                if (co.accessCount < comin.accessCount) {
                    comin = co;
                }
            }
        }

        // 容器没满直接返回
        if (!isFull()) {
            return count;
        }

        // 遍历cache map,移除访问计数值等于或小于最小计数的对象
        // 以最小计数为原点,重新规范计数器
        if (comin != null) {
            long minAccessCount = comin.accessCount;

            values = cacheMap.values().iterator();
            while (values.hasNext()) {
                CacheObject<K, V> co = values.next();
                co.accessCount -= minAccessCount;
                if (co.accessCount <= 0) {
                    values.remove();
                    onRemove(co.key, co.cachedObject);
                    count++;
                }
            }
        }
        return count;
    }
}

FIFOCache

public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {
    
        public FIFOCache(int cacheSize) {
            this(cacheSize, 0);
        }
    
        /**
         * Creates a new LRU cache.
         */
        public FIFOCache(int cacheSize, long timeout) {
            this.cacheSize = cacheSize;
            this.timeout = timeout;
            // 依旧使用LinkedHashMap作为存储,不自动扩容,按插入顺序排序
            cacheMap = new LinkedHashMap<>(cacheSize + 1, 1.0f, false);
        }
    
    
        // ---------------------------------------------------------------- prune
    
        // 移除过期元素,如果空间还是不足,移除最早插入的元素(链表尾部)
        @Override
        protected int pruneCache() {
            int count = 0;
            CacheObject<K,V> first = null;
            Iterator<CacheObject<K,V>> values = cacheMap.values().iterator();
            while (values.hasNext()) {
                CacheObject<K,V> co = values.next();
                if (co.isExpired()) {
                    values.remove();
                    count++;
                }
                if (first == null) {
                    first = co;
                }
            }
            if (isFull()) {
                if (first != null) {
                    cacheMap.remove(first.key);
                    count++;
                }
            }
            return count;
        }
    }
}

TimedCache

public class TimedCache<K, V> extends AbstractCacheMap<K, V> {
        // TimedCache没有容量限制,但必须制定缓存对象的超时时间
        // 定时任务可以根据元素是否超时移除元素
        public TimedCache(long timeout) {
            this.cacheSize = 0;
            this.timeout = timeout;
            cacheMap = new HashMap<>();
        }
    
        // ---------------------------------------------------------------- prune
    
        /**
         * 遍历Map,移除超时元素
         */
        @Override
        protected int pruneCache() {
            int count = 0;
            Iterator<CacheObject<K,V>> values = cacheMap.values().iterator();
            while (values.hasNext()) {
                CacheObject co = values.next();
                if (co.isExpired()) {
                    values.remove();
                    count++;
                }
            }
            return count;
        }
    
    
        // ---------------------------------------------------------------- auto prune
    
        protected Timer pruneTimer;
    
        /**
         * 开启定时清理
         */
        public void schedulePrune(long delay) {
            if (pruneTimer != null) {
                pruneTimer.cancel();
            }
            pruneTimer = new Timer();
            pruneTimer.schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            prune();
                        }
                    }, delay, delay
            );
        }
    
        /**
         * 取消定时清理
         */
        public void cancelPruneSchedule() {
            if (pruneTimer != null) {
                pruneTimer.cancel();
                pruneTimer = null;
            }
        }    
    }
}
上一篇下一篇

猜你喜欢

热点阅读