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; }
时间: 2024-11-10 21:21:29