GUAVA Cache
Guava Cache與ConcurrentMap很相似基於分段鎖及線程安全,但也不完全一樣。最基本的區別是ConcurrentMap會一直保存所有添加的元素,直到顯式地移除。相對地,Guava Cache為了限制內存占用,通常都設定為自動回收元素。在某些場景下,盡管LoadingCache 不回收元素,它也是很有用的,因為它會自動加載緩存。
適用場景
1)可以接受消耗內存來提高性能
2)某些key會被多次查詢
3)緩存中的數據量不會超出內存容量
使用案例
package com.guava.cache; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.junit.Test; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; /** * * @author gaojiayi * */ public class TestCache { //基於loadingCache加載 //@Test public void TestLoadingCache() throws Exception{ LoadingCache<String,String> cahceBuilder=CacheBuilder .newBuilder() .build(new CacheLoader<String, String>(){ //提供默認的加載加載方式,在實際生產中,可以選擇從DB中,或者文件中加載相應的value @Override public String load(String key) throws Exception { String strProValue="hello "+key+"!"; return strProValue; } //批量加載 @Override public Map<String, String> loadAll(Iterable<? extends String> keys) throws Exception { // TODO Auto-generated method stub Map<String, String> retMap = new HashMap<String, String>(); for(String key: keys){ retMap.put(key, load(key)); } return retMap; } }); System.out.println("jerry value:"+cahceBuilder.apply("jerry")); //get(K)方法,這個方法要么返回已經緩存的值,要么使用CacheLoader向緩存原子地加載新值。 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")); //使用cache.put(key, value)方法可以直接向緩存中插入值,這會直接覆蓋掉給定鍵之前映射的值 cahceBuilder.put("harry", "ssdded"); System.out.println("harry value:"+cahceBuilder.get("harry")); System.out.println("________________________________________"); List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); System.out.println(cahceBuilder.getAll(list)); } //基於Callable加載 @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); } }
緩存回收
使用CacheBuilder構建的緩存不會"自動"執行清理和回收工作,也不會在某個緩存項過期后馬上清理,也沒有諸如此類的清理機制。相反,它會在寫操作時順帶做少量的維護工作,或者偶爾在讀操作時做——如果寫操作實在太少的話。
這樣做的原因在於:如果要自動地持續清理緩存,就必須有一個線程,這個線程會和用戶操作競爭共享鎖。此外,某些環境下線程創建可能受限制,這樣CacheBuilder就不可用了。
相反,我們把選擇權交到你手里。如果你的緩存是高吞吐的,那就無需擔心緩存的維護和清理等工作。如果你的 緩存只會偶爾有寫操作,而你又不想清理工作阻礙了讀操作,那么可以創建自己的維護線程,以固定的時間間隔調用Cache.cleanUp()。ScheduledExecutorService可以幫助你很好地實現這樣的定時調度。
基於容量的回收
CacheBuilder.maximumSize(long)。
基於權重回收
//。在權重限定場景中,除了要注意回收也是在重量逼近限定值時就進行了, //還要知道重量是在緩存創建時計算的,因此要考慮重量計算的復雜度。 Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000) .maximumWeight(100) .weigher(new Weigher<String,String>(){ @Override public int weigh(String key, String value) { // TODO Auto-generated method stub return value.getBytes().length; } }).build();
定時回收
- expireAfterAccess(long, TimeUnit):緩存項在給定時間內沒有被讀/寫訪問,則回收。請注意這種緩存的回收順序和基於大小回收一樣。
- expireAfterWrite(long, TimeUnit):緩存項在給定時間內沒有被寫訪問(創建或覆蓋),則回收。如果認為緩存數據總是在固定時候后變得陳舊不可用,這種回收方式是可取的。
基於引用的回收(Reference-based Eviction)
通過使用弱引用的鍵、或弱引用的值、或軟引用的值,Guava Cache可以把緩存設置為允許垃圾回收:
- CacheBuilder.weakKeys():使用弱引用存儲鍵。當鍵沒有其它(強或軟)引用時,緩存項可以被垃圾回收。因為垃圾回收僅依賴恆等式(==),使用弱引用鍵的緩存用==而不是equals比較鍵。
- CacheBuilder.weakValues():使用弱引用存儲值。當值沒有其它(強或軟)引用時,緩存項可以被垃圾回收。因為垃圾回收僅依賴恆等式(==),使用弱引用值的緩存用==而不是equals比較值。
- CacheBuilder.softValues():使用軟引用存儲值。軟引用只有在響應內存需要時,才按照全局最近最少使用的順序回收。考慮到使用軟引用的性能影響,我們通常建議使用更有性能預測性的緩存大小限定(見上文,基於容量回收)。使用軟引用值的緩存同樣用==而不是equals比較值。
顯式清除
任何時候,你都可以顯式地清除緩存項,而不是等到它被回收:
- 個別清除:Cache.invalidate(key)
- 批量清除:Cache.invalidateAll(keys)
- 清除所有緩存項:Cache.invalidateAll()
刷新reload
LoadingCache<String,String> cahceBuilder=CacheBuilder .newBuilder() //注意:緩存並不會因為刷新盲目地定時重置,如果緩存項沒有被檢索,那刷新就不會真的發生, //(可以理解未異步定時獲取新增,而不會做刷新,只有被檢索時才會真正刷新) //如果設置過期(expireAfterWrite)緩存項在過期時間后也變得可以回收。 .refreshAfterWrite(2, TimeUnit.MINUTES) .build(new CacheLoader<String, String>(){ //提供默認的加載加載方式,在實際生產中,可以選擇從DB中,或者文件中加載相應的value @Override public String load(String key) throws Exception { String strProValue="hello "+key+"!"; return strProValue; } //重新加載:重載CacheLoader.reload(K, V)可以擴展刷新時的行為,這個方法允許開發者在計算新值時使用舊的值。 @Override public ListenableFuture<String> reload(String key, String oldValue) throws Exception { // TODO Auto-generated method stub return super.reload(key, oldValue); } });
刪除操作監聽
Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000) .removalListener(new RemovalListener<String,String>(){ @Override public void onRemoval(RemovalNotification<String, String> removalnotification) { // 釋放資源 }})
參考:http://www.cnblogs.com/peida/p/Guava_Cache.html
