Jodd cache提供了一组cache的实现,其层次如下:

其中,

AbstractCacheMap是一个具有计时和大小的缓存map的默认实现,它的实现类必须:

  创建一个新的缓存map。

  实现自己的删除(prune)策略。

内部使用ReentranReadWriteLock来同步。因为从一个读锁升级到一个写锁是不可能的,因此在get(Object)方法内要注意。

FIFOCach:先进先出缓存。优点是简单高效。缺点是不灵活,没有在内存中保存常用的缓存对象。

/**
     * Creates a new LRU cache.
     */
    public FIFOCache(int cacheSize, long timeout) {
        this.cacheSize = cacheSize;
        this.timeout = timeout;
        cacheMap = new LinkedHashMap<K,CacheObject<K,V>>(cacheSize + 1, 1.0f, false);
    }


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

    /**
     * Prune expired objects and, if cache is still full, the first one.
     */
    @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() == true) {
                values.remove();
                count++;
            }
            if (first == null) {
                first = co;
            }
        }
        if (isFull()) {
            if (first != null) {
                cacheMap.remove(first.key);
                count++;
            }
        }
        return count;
    }

LFUCache:最少访问次数缓存。优点是常用缓存保留在内存中,偶然会使扫描算法失效。缺点是大的获取消耗即这个算法不能快速适应变化的使用模式,特别是集群的临时获取是无效的。

public LFUCache(int maxSize, long timeout) {
        this.cacheSize = maxSize;
        this.timeout = timeout;
        cacheMap = new HashMap<K, CacheObject<K,V>>(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();
        while (values.hasNext()) {
            CacheObject<K,V> co = values.next();
            if (co.isExpired() == true) {
                values.remove();
                onRemove(co.key, co.cachedObject);
                count++;
                continue;
            }
            
            if (comin == null) {
                comin = co;
            } else {
                if (co.accessCount < comin.accessCount) {
                    comin = co;
                }
            }
        }

        if (isFull() == false) {
            return count;
        }

        // decrease access count to all cached objects
        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;
    }

LRUCache:最近未访问缓存。缓存对象的消耗是一个常量。简单高效,比FIFO更适应一个变化的场景。缺点是可能会被不会重新访问的缓存占满空间,特别是在面对获取类型扫描时则完全不起作用。然后它是目前最常用的缓存算法。

/**
     * Creates a new LRU cache.
     */
    public LRUCache(int cacheSize, long timeout) {
        this.cacheSize = cacheSize;
        this.timeout = timeout;
        cacheMap = new LinkedHashMap<K, CacheObject<K,V>>(cacheSize + 1, 1.0f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                return LRUCache.this.removeEldestEntry(size());
            }
        };
    }

    /**
     * Removes the eldest entry if current cache size exceed cache size.
     */
    protected boolean removeEldestEntry(int currentSize) {
        if (cacheSize == 0) {
            return false;
        }
        return currentSize > cacheSize;
    }

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

    /**
     * Prune only expired objects, <code>LinkedHashMap</code> will take care of LRU if needed.
     */
    @Override
    protected int pruneCache() {
        if (isPruneExpiredActive() == false) {
            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() == true) {
                values.remove();
                count++;
            }
        }
        return count;
    }

TimedCache 不限制大小,只有当对象过期时才会删除。标准的chache方法不会显式的调用删除(prune),而是根据定义好的延迟进行定时删除。

public TimedCache(long timeout) {
        this.cacheSize = 0;
        this.timeout = timeout;
        cacheMap = new HashMap<K, CacheObject<K,V>>();
    }

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

    /**
     * Prunes expired elements from the cache. Returns the number of removed objects.
     */
    @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() == true) {
                values.remove();
                count++;
            }
        }
        return count;
    }


    // ---------------------------------------------------------------- auto prune

    protected Timer pruneTimer;

    /**
     * Schedules prune.
     */
    public void schedulePrune(long delay) {
        if (pruneTimer != null) {
            pruneTimer.cancel();
        }
        pruneTimer = new Timer();
        pruneTimer.schedule(
                new TimerTask() {
                    @Override
                    public void run() {
                        prune();
                    }
                }, delay, delay
        );
    }

    /**
     * Cancels prune schedules.
     */
    public void cancelPruneSchedule() {
        if (pruneTimer != null) {
            pruneTimer.cancel();
            pruneTimer = null;
        }
    }

注意,还提供了一个FileLFUCache,没有继承AbstractCacheMap.用LFU将文件缓存到内存,极大加快访问常用文件的性能。

    protected final LFUCache<File, byte[]> cache;
    protected final int maxSize;
    protected final int maxFileSize;

    protected int usedSize;

    /**
     * Creates file LFU cache with specified size. Sets
     * {@link #maxFileSize max available file size} to half of this value.
     */
    public FileLFUCache(int maxSize) {
        this(maxSize, maxSize / 2, 0);
    }

    public FileLFUCache(int maxSize, int maxFileSize) {
        this(maxSize, maxFileSize, 0);
    }

    /**
     * Creates new File LFU cache.
     * @param maxSize total cache size in bytes
     * @param maxFileSize max available file size in bytes, may be 0
     * @param timeout timeout, may be 0
     */
    public FileLFUCache(int maxSize, int maxFileSize, long timeout) {
        this.cache = new LFUCache<File, byte[]>(0, timeout) {
            @Override
            public boolean isFull() {
                return usedSize > FileLFUCache.this.maxSize;
            }

            @Override
            protected void onRemove(File key, byte[] cachedObject) {
                usedSize -= cachedObject.length;
            }

        };
        this.maxSize = maxSize;
        this.maxFileSize = maxFileSize;
    }

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

    /**
     * Returns max cache size in bytes.
     */
    public int getMaxSize() {
        return maxSize;
    }

    /**
     * Returns actually used size in bytes.
     */
    public int getUsedSize() {
        return usedSize;
    }

    /**
     * Returns maximum allowed file size that can be added to the cache.
     * Files larger than this value will be not added, even if there is
     * enough room.
     */
    public int getMaxFileSize() {
        return maxFileSize;
    }

    /**
     * Returns number of cached files.
     */
    public int getCachedFilesCount() {
        return cache.size();
    }

    /**
     * Returns timeout.
     */
    public long getCacheTimeout() {
        return cache.getCacheTimeout();
    }

    /**
     * Clears the cache.
     */
    public void clear() {
        cache.clear();
        usedSize = 0;
    }

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

    public byte[] getFileBytes(String fileName) throws IOException {
        return getFileBytes(new File(fileName));
    }

    /**
     * Returns cached file bytes.
     */
    public byte[] getFileBytes(File file) throws IOException {
        byte[] bytes = cache.get(file);
        if (bytes != null) {
            return bytes;
        }

        // add file
        bytes = FileUtil.readBytes(file);

        if ((maxFileSize != 0) && (file.length() > maxFileSize)) {
            // don't cache files that size exceed max allowed file size
            return bytes;
        }

        usedSize += bytes.length;

        // put file into cache
        // if used size > total, purge() will be invoked
        cache.put(file, bytes);

        return bytes;
    }

 

posted on 2015-07-16 11:34  一天不进步,就是退步  阅读(1304)  评论(0编辑  收藏  举报