緩存,在咱們平常開發中是必不可少的一種解決性能問題的方法。簡單的說,cache 就是爲了提高系統性能而開闢的一塊內存空間。java
緩存的主要做用是暫時在內存中保存業務系統的數據處理結果,而且等待下次訪問使用。在平常開發的不少場合,因爲受限於硬盤IO的性能或者咱們自 身業務系統的數據處理和獲取可能很是費時,當咱們發現咱們的系統這個數據請求量很大的時候,頻繁的IO和頻繁的邏輯處理會致使硬盤和CPU資源的瓶頸出 現。緩存的做用就是將這些來自不易的數據保存在內存中,當有其餘線程或者客戶端須要查詢相同的數據資源時,直接從緩存的內存塊中返回數據,這樣不但能夠提 高系統的響應時間,同時也能夠節省對這些數據的處理流程的資源消耗,總體上來講,系統性能會有大大的提高。數據庫
緩存在不少系統和架構中都用普遍的應用,例如:緩存
1.CPU緩存
2.操做系統緩存
3.本地緩存
4.分佈式緩存
5.HTTP緩存
6.數據庫緩存
等等,能夠說在計算機和網絡領域,緩存無處不在。能夠這麼說,只要有硬件性能不對等,涉及到網絡傳輸的地方都會有緩存的身影。安全
Guava Cache是一個全內存的本地緩存實現,它提供了線程安全的實現機制。總體上來講Guava cache 是本地緩存的不二之選,簡單易用,性能好。網絡
Guava Cache有兩種建立方式:架構
1. cacheLoader
2. callable callbackapp
經過這兩種方法建立的cache,和一般用map來緩存的作法比,不一樣在於,這兩種方法都實現了一種邏輯——從緩存中取key X的值,若是該值已經緩存過了,則返回緩存中的值,若是沒有緩存過,能夠經過某個方法來獲取這個值。但不一樣的在於cacheloader的定義比較寬泛, 是針對整個cache定義的,能夠認爲是統一的根據key值load value的方法。而callable的方式較爲靈活,容許你在get的時候指定。異步
cacheLoader方式實現實例:async
@Test public void TestLoadingCache() throws Exception{ LoadingCache<String,String> cahceBuilder=CacheBuilder .newBuilder() .build(new CacheLoader<String, String>(){ @Override public String load(String key) throws Exception { String strProValue="hello "+key+"!"; return strProValue; } }); System.out.println("jerry value:"+cahceBuilder.apply("jerry")); System.out.println("jerry value:"+cahceBuilder.get("jerry")); System.out.println("peida value:"+cahceBuilder.get("peida")); System.out.println("peida value:"+cahceBuilder.apply("peida")); System.out.println("lisa value:"+cahceBuilder.apply("lisa")); cahceBuilder.put("harry", "ssdded"); System.out.println("harry value:"+cahceBuilder.get("harry")); }
輸出:分佈式
jerry value:hello jerry!jerry value:hello jerry!peida value:hello peida!peida value:hello peida!lisa value:hello lisa!harry value:ssdded
callable callback的實現:
@Test public void testcallableCache()throws Exception{ Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000).build(); String resultVal = cache.get("jerry", new Callable<String>() { public String call() { String strProValue="hello "+"jerry"+"!"; return strProValue; } }); System.out.println("jerry value : " + resultVal); resultVal = cache.get("peida", new Callable<String>() { public String call() { String strProValue="hello "+"peida"+"!"; return strProValue; } }); System.out.println("peida value : " + resultVal); } 輸出: jerry value : hello jerry! peida value : hello peida!
cache的參數說明:
回收的參數:
1. 大小的設置:CacheBuilder.maximumSize(long) CacheBuilder.weigher(Weigher) CacheBuilder.maxumumWeigher(long)
2. 時間:expireAfterAccess(long, TimeUnit) expireAfterWrite(long, TimeUnit)
3. 引用:CacheBuilder.weakKeys() CacheBuilder.weakValues() CacheBuilder.softValues()
4. 明確的刪除:invalidate(key) invalidateAll(keys) invalidateAll()
5. 刪除監聽器:CacheBuilder.removalListener(RemovalListener)
refresh機制:
1. LoadingCache.refresh(K) 在生成新的value的時候,舊的value依然會被使用。
2. CacheLoader.reload(K, V) 生成新的value過程當中容許使用舊的value
3. CacheBuilder.refreshAfterWrite(long, TimeUnit) 自動刷新cache
基於泛型的實現:
/** * 不須要延遲處理(泛型的方式封裝) * @return */ public <K , V> LoadingCache<K , V> cached(CacheLoader<K , V> cacheLoader) { LoadingCache<K , V> cache = CacheBuilder .newBuilder() .maximumSize(2) .weakKeys() .softValues() .refreshAfterWrite(120, TimeUnit.SECONDS) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(new RemovalListener<K, V>(){ @Override public void onRemoval(RemovalNotification<K, V> rn) { System.out.println(rn.getKey()+"被移除"); }}) .build(cacheLoader); return cache; } /** * 經過key獲取value * 調用方式 commonCache.get(key) ; return String * @param key * @return * @throws Exception */ public LoadingCache<String , String> commonCache(final String key) throws Exception{ LoadingCache<String , String> commonCache= cached(new CacheLoader<String , String>(){ @Override public String load(String key) throws Exception { return "hello "+key+"!"; } }); return commonCache; } @Test public void testCache() throws Exception{ LoadingCache<String , String> commonCache=commonCache("peida"); System.out.println("peida:"+commonCache.get("peida")); commonCache.apply("harry"); System.out.println("harry:"+commonCache.get("harry")); commonCache.apply("lisa"); System.out.println("lisa:"+commonCache.get("lisa")); }
輸出:
peida:hello peida!harry:hello harry!peida被移除 lisa:hello lisa!
基於泛型的Callable Cache實現:
private static Cache<String, String> cacheFormCallable = null; /** * 對須要延遲處理的能夠採用這個機制;(泛型的方式封裝) * @param <K> * @param <V> * @param key * @param callable * @return V * @throws Exception */ public static <K,V> Cache<K , V> callableCached() throws Exception { Cache<K, V> cache = CacheBuilder .newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); return cache; } private String getCallableCache(final String userName) { try { //Callable只有在緩存值不存在時,纔會調用 return cacheFormCallable.get(userName, new Callable<String>() { @Override public String call() throws Exception { System.out.println(userName+" from db"); return "hello "+userName+"!"; } }); } catch (ExecutionException e) { e.printStackTrace(); return null; } } @Test public void testCallableCache() throws Exception{ final String u1name = "peida"; final String u2name = "jerry"; final String u3name = "lisa"; cacheFormCallable=callableCached(); System.out.println("peida:"+getCallableCache(u1name)); System.out.println("jerry:"+getCallableCache(u2name)); System.out.println("lisa:"+getCallableCache(u3name)); System.out.println("peida:"+getCallableCache(u1name)); }
輸出:
peida from db peida:hello peida!jerry from db jerry:hello jerry!lisa from db lisa:hello lisa!peida:hello peida!
說明:Callable只有在緩存值不存在時,纔會調用,好比第二次調用getCallableCache(u1name)直接返回緩存中的值
guava Cache數據移除:
guava作cache時候數據的移除方式,在guava中數據的移除分爲被動移除和主動移除兩種。
被動移除數據的方式,guava默認提供了三種方式:
1.基於大小的移除:看字面意思就知道就是按照緩存的大小來移除,若是即將到達指定的大小,那就會把不經常使用的鍵值對從cache中移除。
定義的方式通常爲 CacheBuilder.maximumSize(long),還有一種一種能夠算權重的方法,我的認爲實際使用中不太用到。就這個經常使用的來看有幾個注意點,
其一,這個size指的是cache中的條目數,不是內存大小或是其餘;
其二,並非徹底到了指定的size系統纔開始移除不經常使用的數據的,而是接近這個size的時候系統就會開始作移除的動做;
其三,若是一個鍵值對已經從緩存中被移除了,你再次請求訪問的時候,若是cachebuild是使用cacheloader方式的,那依然仍是會從cacheloader中再取一次值,若是這樣尚未,就會拋出異常
2.基於時間的移除:guava提供了兩個基於時間移除的方法
expireAfterAccess(long, TimeUnit) 這個方法是根據某個鍵值對最後一次訪問以後多少時間後移除
expireAfterWrite(long, TimeUnit) 這個方法是根據某個鍵值對被建立或值被替換後多少時間移除
3.基於引用的移除:
這種移除方式主要是基於java的垃圾回收機制,根據鍵或者值的引用關係決定移除
主動移除數據方式,主動移除有三種方法: 1.單獨移除用 Cache.invalidate(key) 2.批量移除用 Cache.invalidateAll(keys) 3.移除全部用 Cache.invalidateAll() 若是須要在移除數據的時候有所動做還能夠定義Removal Listener,可是有點須要注意的是默認Removal Listener中的行爲是和移除動做同步執行的,若是須要改爲異步形式,能夠考慮使用 RemovalListeners.asynchronous(RemovalListener, Executor)