您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關LocalCache在Java項目中如何實現本地緩存,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
一、本地緩存應用場景
localcache有著極大的性能優勢:
1. 單機情況下適當使用localcache會使應用的性能得到很大的提升。
2. 集群環境下對于敏感性要求不高的數據可以使用localcache,只配置簡單的失效機制來保證數據的相對一致性。
哪些數據可以存儲到本地緩存?
1.訪問頻繁的數據;
2.靜態基礎數據(長時間內不變的數據);
3.相對靜態數據(短時間內不變的數據)。
二、java本地緩存標準
Java緩存新標準(javax.cache),這個標準由JSR107所提出,已經被包含在Java EE 7中。
特性:
1.原子操作,跟java.util.ConcurrentMap類似
2.從緩存中讀取
3.寫入緩存
4.緩存事件監聽器
5.數據統計
6.包含所有隔離(ioslation)級別的事務
7.緩存注解(annotations)
8.保存定義key和值類型的泛型緩存
9.引用保存(只適用于堆緩存)和值保存定義
但目前應用不是很普遍。
三、java開源緩存框架
比較有名的本地緩存開源框架有:
1.EHCache
EHCache是一個純java的在進程中的緩存,它具有以下特性:快速,簡單,為Hibernate2.1充當可插入的緩存,最小的依賴性,全面的文檔和測試。
BUG: 過期失效的緩存元素無法被GC掉,時間越長緩存越多,內存占用越大,導致內存泄漏的概率越大。
2.OSCache
OSCache有以下特點:緩存任何對象,你可以不受限制的緩存部分jsp頁面或HTTP請求,任何java對象都可以緩存。擁有全面的API--OSCache API給你全面的程序來控制所有的OSCache特性。永久緩存--緩存能隨意的寫入硬盤,因此允許昂貴的創建(expensive-to-create)數據來保持緩存,甚至能讓應用重啟。支持集群--集群緩存數據能被單個的進行參數配置,不需要修改代碼。緩存記錄的過期--你可以有最大限度的控制緩存對象的過期,包括可插入式的刷新策略(如果默認性能不需要時)。
3.JCache
Java緩存新標準(javax.cache)
4.cache4j
cache4j是一個有簡單API與實現快速的Java對象緩存。它的特性包括:在內存中進行緩存,設計用于多線程環境,兩種實現:同步與阻塞,多種緩存清除策略:LFU, LRU, FIFO,可使用強引用。
5.ShiftOne
ShiftOne Java Object Cache是一個執行一系列嚴格的對象緩存策略的Java lib,就像一個輕量級的配置緩存工作狀態的框架。
6.WhirlyCache
Whirlycache是一個快速的、可配置的、存在于內存中的對象的緩存。
四、LocalCache實現
1、LocalCache簡介
LocalCache是一個精簡版本地緩存組件,有以下特點:
1. 有容量上限maxCapacity;
2. 緩存達到容量上限時基于LRU策略來移除緩存元素;
3. 緩存對象的生命周期(緩存失效時間)由調用方決定;
4. 緩存對象失效后,將會有定時清理線程來清理掉,不會導致內存泄漏。
5. 性能比Ehcache稍強。
2、總體設計
LocalCache總體設計:
1. 緩存元素 CacheElement;
2. 緩存容器 LRULinkedHashMap;
3. 緩存接口 Cache;
4. 緩存組件實現 LocalCache。
3、詳細設計
1. CacheElement設計
/** * 緩存元素 * */ public class CacheElement { private Object key; private Object value; private long createTime; private long lifeTime; private int hitCount; public CacheElement() { } public CacheElement(Object key ,Object value) { this.key = key; this.value = value; this.createTime = System.currentTimeMillis(); } public Object getKey() { return key; } public void setKey(Object key) { this.key = key; } public Object getValue() { hitCount++; return value; } public void setValue(Object value) { this.value = value; } public long getCreateTime() { return createTime; } public void setCreateTime(long createTime) { this.createTime = createTime; } public int getHitCount() { return hitCount; } public void setHitCount(int hitCount) { this.hitCount = hitCount; } public long getLifeTime() { return lifeTime; } public void setLifeTime(long lifeTime) { this.lifeTime = lifeTime; } public boolean isExpired() { boolean isExpired = System.currentTimeMillis() - getCreateTime() > getLifeTime(); return isExpired; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("[ key=").append(key).append(", isExpired=").append(isExpired()) .append(", lifeTime=").append(lifeTime).append(", createTime=").append(createTime) .append(", hitCount=").append(hitCount) .append(", value=").append(value).append(" ]"); return sb.toString(); } /* * (non-Javadoc) * @see java.lang.Object#hashCode() */ public final int hashCode(){ if(null == key){ return "".hashCode(); } return this.key.hashCode(); } /* * (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public final boolean equals(Object object) { if ((object == null) || (!(object instanceof CacheElement))) { return false; } CacheElement element = (CacheElement) object; if ((this.key == null) || (element.getKey() == null)) { return false; } return this.key.equals(element.getKey()); } }
2. LRULinkedHashMap實現
import java.util.LinkedHashMap; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 實現 LRU策略的 LinkedHashMap * * @param <K> * @param <V> */ public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { protected static final long serialVersionUID = 2828675280716975892L; protected static final int DEFAULT_MAX_ENTRIES = 100; protected final int initialCapacity; protected final int maxCapacity; protected boolean enableRemoveEldestEntry = true;//是否允許自動移除比較舊的元素(添加元素時) protected static final float DEFAULT_LOAD_FACTOR = 0.8f; protected final Lock lock = new ReentrantLock(); public LRULinkedHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_MAX_ENTRIES); } public LRULinkedHashMap(int initialCapacity ,int maxCapacity) { //set accessOrder=true, LRU super(initialCapacity, DEFAULT_LOAD_FACTOR, true); this.initialCapacity = initialCapacity; this.maxCapacity = maxCapacity; } /* * (non-Javadoc) * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) */ protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { return enableRemoveEldestEntry && ( size() > maxCapacity ); } /* * (non-Javadoc) * @see java.util.LinkedHashMap#get(java.lang.Object) */ public V get(Object key) { try { lock.lock(); return super.get(key); } finally { lock.unlock(); } } /* * (non-Javadoc) * @see java.util.HashMap#put(java.lang.Object, java.lang.Object) */ public V put(K key, V value) { try { lock.lock(); return super.put(key, value); } finally { lock.unlock(); } } /* * (non-Javadoc) * @see java.util.HashMap#remove(java.lang.Object) */ public V remove(Object key) { try { lock.lock(); return super.remove(key); } finally { lock.unlock(); } } /* * (non-Javadoc) * @see java.util.LinkedHashMap#clear() */ public void clear() { try { lock.lock(); super.clear(); } finally { lock.unlock(); } } /* * (non-Javadoc) * @see java.util.HashMap#keySet() */ public Set<K> keySet() { try { lock.lock(); return super.keySet(); } finally { lock.unlock(); } } public boolean isEnableRemoveEldestEntry() { return enableRemoveEldestEntry; } public void setEnableRemoveEldestEntry(boolean enableRemoveEldestEntry) { this.enableRemoveEldestEntry = enableRemoveEldestEntry; } public int getInitialCapacity() { return initialCapacity; } public int getMaxCapacity() { return maxCapacity; } }
3. Cache接口設計
/** * 緩存接口 * */ public interface Cache { /** * 獲取緩存 * @param key * @return */ public <T> T getCache(Object key); /** * 緩存對象 * @param key * @param value * @param milliSecond 緩存生命周期(毫秒) */ public void putCache(Object key, Object value ,Long milliSecond); /** * 緩存容器中是否包含 key * @param key * @return */ public boolean containsKey(Object key); /** * 緩存列表大小 * @return */ public int getSize(); /** * 是否啟用緩存 */ public boolean isEnabled(); /** * 啟用 或 停止 * @param enable */ public void setEnabled(boolean enabled); /** * 移除所有緩存 */ public void invalidateCaches(); /** * 移除 指定key緩存 * @param key */ public void invalidateCache(Object key); }
4. LocalCache實現
import java.util.Date; import java.util.Iterator; import java.util.Random; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 本地緩存組件 */ public class LocalCache implements Cache{ private Logger logger = LoggerFactory.getLogger(this.getClass()); private LRULinkedHashMap<Object, CacheElement> cacheMap; protected boolean initFlag = false;//初始化標識 protected final long defaultLifeTime = 5 * 60 * 1000;//5分鐘 protected boolean warnLongerLifeTime = false; protected final int DEFAULT_INITIAL_CAPACITY = 100; protected final int DEFAULT_MAX_CAPACITY = 100000; protected int initialCapacity = DEFAULT_INITIAL_CAPACITY;//初始化緩存容量 protected int maxCapacity = DEFAULT_MAX_CAPACITY;//最大緩存容量 protected int timeout = 20;//存取緩存操作響應超時時間(毫秒數) private boolean enabled = true; private Thread gcThread = null; private String lastGCInfo = null;//最后一次GC清理信息{ size, removeCount, time ,nowTime} private boolean logGCDetail = false;//記錄gc清理細節 private boolean enableGC = true;//是否允許清理的緩存(添加元素時) private int gcMode = 0;//清理過期元素模式 { 0=迭代模式 ; 1=隨機模式 } private int gcIntervalTime = 2 * 60 * 1000;//間隔時間(分鐘) private boolean iterateScanAll = true;//是否迭代掃描全部 private float gcFactor = 0.5F;//清理百分比 private int maxIterateSize = DEFAULT_MAX_CAPACITY/2;//迭代模式下一次最大迭代數量 private volatile int iterateLastIndex = 0;//最后迭代下標 private int maxRandomTimes = 100;//隨機模式下最大隨機次數 protected final static Random random = new Random(); private static LocalCache instance = new LocalCache(); public static LocalCache getInstance() { return instance; } private LocalCache(){ this.init(); } protected synchronized void init() { if(initFlag){ logger.warn("init repeat."); return ; } this.initCache(); this.startGCDaemonThread(); initFlag = true; if(logger.isInfoEnabled()){ logger.info("init -- OK"); } } private void startGCDaemonThread(){ if(initFlag){ return ; } this.maxIterateSize = maxCapacity /2; try{ this.gcThread = new Thread() { public void run() { logger.info("[" + (Thread.currentThread().getName()) + "]start..."); //sleep try { Thread.sleep(getGcIntervalTime() < 30000 ? 30000 : getGcIntervalTime()); } catch (Exception e) { e.printStackTrace(); } while( true ){ //gc gc(); //sleep try { Thread.sleep(getGcIntervalTime() < 30000 ? 30000 : getGcIntervalTime()); } catch (Exception e) { e.printStackTrace(); } } } }; this.gcThread.setName("localCache-gcThread"); this.gcThread.setDaemon(true); this.gcThread.start(); if(logger.isInfoEnabled()){ logger.info("startGCDaemonThread -- OK"); } }catch(Exception e){ logger.error("[localCache gc]DaemonThread -- error: " + e.getMessage(), e); } } private void initCache(){ if(initFlag){ return ; } initialCapacity = (initialCapacity <= 0 ? DEFAULT_INITIAL_CAPACITY : initialCapacity); maxCapacity = (maxCapacity < initialCapacity ? DEFAULT_MAX_CAPACITY : maxCapacity); cacheMap = new LRULinkedHashMap<Object, CacheElement>(initialCapacity ,maxCapacity); if(logger.isInfoEnabled()){ logger.info("initCache -- OK"); } } /* * (non-Javadoc) */ @SuppressWarnings("unchecked") public <T> T getCache(Object key) { if(!isEnabled()){ return null; } long st = System.currentTimeMillis(); T objValue = null; CacheElement cacheObj = cacheMap.get(key); if (isExpiredCache(cacheObj)) { cacheMap.remove(key); }else { objValue = (T) (cacheObj == null ? null : cacheObj.getValue()); } long et = System.currentTimeMillis(); if((et - st)>timeout){ if(this.logger.isWarnEnabled()){ this.logger.warn("getCache_timeout_" + (et - st) + "_[" + key + "]"); } } if(logger.isDebugEnabled()){ String message = ("get( " + key + ") return: " + objValue); logger.debug(message); } return objValue; } /* * (non-Javadoc) */ public void putCache(Object key, Object value ,Long lifeTime) { if(!isEnabled()){ return; } Long st = System.currentTimeMillis(); lifeTime = (null == lifeTime ? defaultLifeTime : lifeTime); CacheElement cacheObj = new CacheElement(); cacheObj.setCreateTime(System.currentTimeMillis()); cacheObj.setLifeTime(lifeTime); cacheObj.setValue(value); cacheObj.setKey(key); cacheMap.put(key, cacheObj); long et = System.currentTimeMillis(); if((et - st)>timeout){ if(this.logger.isWarnEnabled()){ this.logger.warn("putCache_timeout_" + (et - st) + "_[" + key + "]"); } } if(logger.isDebugEnabled()){ String message = ("putCache( " + cacheObj + " ) , 耗時 " + (et - st) + "(毫秒)."); logger.debug(message); } if(lifeTime > defaultLifeTime && this.isWarnLongerLifeTime()){ if(logger.isWarnEnabled()){ String message = ("LifeTime[" + (lifeTime/1000) + "秒] too long for putCache(" + cacheObj + ")"); logger.warn(message); } } } /** * key 是否過期 * @param key * @return */ protected boolean isExpiredKey(Object key) { CacheElement cacheObj = cacheMap.get(key); return this.isExpiredCache(cacheObj); } /** * cacheObj 是否過期 * @param key * @return */ protected boolean isExpiredCache(CacheElement cacheObj) { if (cacheObj == null) { return false; } return cacheObj.isExpired(); } /* * (non-Javadoc) */ public void invalidateCaches(){ try{ cacheMap.clear(); }catch(Exception e){ e.printStackTrace(); } } /* * (non-Javadoc) */ public void invalidateCache(Object key){ try{ cacheMap.remove(key); }catch(Exception e){ e.printStackTrace(); } } /* * (non-Javadoc) */ public boolean containsKey(Object key) { return cacheMap.containsKey(key); } /* * (non-Javadoc) */ public int getSize() { return cacheMap.size(); } /* * (non-Javadoc) */ public Iterator<Object> getKeyIterator() { return cacheMap.keySet().iterator(); } /* * (non-Javadoc) */ public boolean isEnabled() { return this.enabled; } /* * (non-Javadoc) */ public void setEnabled(boolean enabled) { this.enabled = enabled; if(!this.enabled){ //清理緩存 this.invalidateCaches(); } } /** * 清理過期緩存 */ protected synchronized boolean gc(){ if(!isEnableGC()){ return false; } try{ iterateRemoveExpiredCache(); }catch(Exception e){ logger.error("gc() has error: " + e.getMessage(), e); } return true; } /** * 迭代模式 - 移除過期的 key * @param exceptKey */ private void iterateRemoveExpiredCache(){ long startTime = System.currentTimeMillis(); int size = cacheMap.size(); if(size ==0){ return; } int keyCount = 0; int removedCount = 0 ; int startIndex = 0; int endIndex = 0; try{ Object [] keys = cacheMap.keySet().toArray(); keyCount = keys.length; int maxIndex = keyCount -1 ; //初始化掃描下標 if(iterateScanAll){ startIndex = 0; endIndex = maxIndex; }else { int gcThreshold = this.getGcThreshold(); int iterateLen = gcThreshold > this.maxIterateSize ? this.maxIterateSize : gcThreshold; startIndex = this.iterateLastIndex; startIndex = ( (startIndex < 0 || startIndex > maxIndex) ? 0 : startIndex ); endIndex = (startIndex + iterateLen); endIndex = (endIndex > maxIndex ? maxIndex : endIndex); } //迭代清理 boolean flag = false; for(int i=startIndex; i<= endIndex; i++){ flag = this.removeExpiredKey(keys[i]); if(flag){ removedCount++; } } this.iterateLastIndex = endIndex; keys = null; }catch(Exception e){ logger.error("iterateRemoveExpiredCache -- 移除過期的 key時出現異常: " + e.getMessage(), e); } long endTime = System.currentTimeMillis(); StringBuffer sb = new StringBuffer(); sb.append("iterateRemoveExpiredCache [ size: ").append(size).append(", keyCount: ").append(keyCount) .append(", startIndex: ").append(startIndex).append(", endIndex: ").append(iterateLastIndex) .append(", removedCount: ").append(removedCount).append(", currentSize: ").append(this.cacheMap.size()) .append(", timeConsuming: ").append(endTime - startTime).append(", nowTime: ").append(new Date()) .append(" ]"); this.lastGCInfo = sb.toString(); if(logger.isInfoEnabled()){ logger.info("iterateRemoveExpiredCache -- 清理結果 -- "+ lastGCInfo); } } /** * 隨機模式 - 移除過期的 key */ private void randomRemoveExpiredCache(){ long startTime = System.currentTimeMillis(); int size = cacheMap.size(); if(size ==0){ return; } int removedCount = 0 ; try{ Object [] keys = cacheMap.keySet().toArray(); int keyCount = keys.length; boolean removeFlag = false; int removeRandomTimes = this.getGcThreshold(); removeRandomTimes = ( removeRandomTimes > this.getMaxRandomTimes() ? this.getMaxRandomTimes() : removeRandomTimes ); while(removeRandomTimes-- > 0){ int index = random.nextInt(keyCount); boolean flag = this.removeExpiredKey(keys[index]); if(flag){ removeFlag = true; removedCount ++; } } //嘗試 移除 首尾元素 if(!removeFlag){ this.removeExpiredKey(keys[0]); this.removeExpiredKey(keys[keyCount-1]); } keys=null; }catch(Exception e){ logger.error("randomRemoveExpiredCache -- 移除過期的 key時出現異常: " + e.getMessage(), e); } long endTime = System.currentTimeMillis(); StringBuffer sb = new StringBuffer(); sb.append("randomRemoveExpiredCache [ size: ").append(size).append(", removedCount: ").append(removedCount) .append(", currentSize: ").append(this.cacheMap.size()).append(", timeConsuming: ").append(endTime - startTime) .append(", nowTime: ").append(new Date()) .append(" ]"); this.lastGCInfo = sb.toString(); if(logger.isInfoEnabled()){ logger.info("randomRemoveExpiredCache -- 清理結果 -- "+ lastGCInfo); } } private boolean removeExpiredKey(Object key){ boolean flag = false; CacheElement cacheObj = null; if(null != key){ try{ cacheObj = cacheMap.get(key); boolean isExpiredCache = this.isExpiredCache(cacheObj); if(isExpiredCache){ cacheMap.remove(key); flag = true; } }catch(Exception e){ logger.error("removeExpired(" + key + ") -- error: " + e.getMessage(), e); } } if(!flag && logGCDetail){ this.logger.warn("removeExpiredKey(" + key + ") return [" + flag + "]--" + cacheObj); } return flag; } public int getInitialCapacity() { return initialCapacity; } public int getMaxCapacity() { return maxCapacity; } public int getGcMode() { return gcMode; } public void setGcMode(int gcMode) { this.gcMode = gcMode; } public int getGcIntervalTime() { return gcIntervalTime; } public void setGcIntervalTime(int gcIntervalTime) { this.gcIntervalTime = gcIntervalTime; } public boolean isEnableGC() { return enableGC; } public void setEnableGC(boolean enableGC) { this.enableGC = enableGC; } public boolean isIterateScanAll() { return iterateScanAll; } public void setIterateScanAll(boolean iterateScanAll) { this.iterateScanAll = iterateScanAll; } public float getGcFactor() { return gcFactor; } public void setGcFactor(float gcFactor) { this.gcFactor = gcFactor; } /** * gc 閥值 * @return */ public int getGcThreshold() { int threshold = (int)( this.cacheMap.getMaxCapacity() * gcFactor ); return threshold; } public String getLastGCInfo() { return lastGCInfo; } public void setLastGCInfo(String lastGCInfo) { this.lastGCInfo = lastGCInfo; } public boolean isLogGCDetail() { return logGCDetail; } public void setLogGCDetail(boolean logGCDetail) { this.logGCDetail = logGCDetail; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public int getMaxIterateSize() { return maxIterateSize; } public void setMaxIterateSize(int maxIterateSize) { this.maxIterateSize = maxIterateSize; } public int getMaxRandomTimes() { return maxRandomTimes; } public void setMaxRandomTimes(int maxRandomTimes) { this.maxRandomTimes = maxRandomTimes; } public boolean isInitFlag() { return initFlag; } public long getDefaultLifeTime() { return defaultLifeTime; } public boolean isWarnLongerLifeTime() { return warnLongerLifeTime; } public void setWarnLongerLifeTime(boolean warnLongerLifeTime) { this.warnLongerLifeTime = warnLongerLifeTime; } //======================== dynMaxCapacity ======================== private int dynMaxCapacity = maxCapacity; public int getDynMaxCapacity() { return dynMaxCapacity; } public void setDynMaxCapacity(int dynMaxCapacity) { this.dynMaxCapacity = dynMaxCapacity; } public void resetMaxCapacity(){ if(dynMaxCapacity > initialCapacity && dynMaxCapacity != maxCapacity){ if(logger.isInfoEnabled()){ logger.info("resetMaxCapacity( " + dynMaxCapacity + " ) start..."); } synchronized(cacheMap){ LRULinkedHashMap<Object, CacheElement> cacheMap0 = new LRULinkedHashMap<Object, CacheElement>(initialCapacity ,dynMaxCapacity); cacheMap.clear(); cacheMap = cacheMap0; this.maxCapacity = dynMaxCapacity; } if(logger.isInfoEnabled()){ logger.info("resetMaxCapacity( " + dynMaxCapacity + " ) OK."); } }else { if(logger.isWarnEnabled()){ logger.warn("resetMaxCapacity( " + dynMaxCapacity + " ) NO."); } } } //======================== showCacheElement ======================== private String showCacheKey; public String getShowCacheKey() { return showCacheKey; } public void setShowCacheKey(String showCacheKey) { this.showCacheKey = showCacheKey; } public Object showCacheElement(){ Object v = null; if(null != this.showCacheKey){ v = cacheMap.get(showCacheKey); } return v; } }
看完上述內容,你們對LocalCache在Java項目中如何實現本地緩存有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。