緩存在不少場景下都是至關有用的。例如,計算或檢索一個值的代價很高,而且對一樣的輸入須要不止一次獲取 值的時候,就應當考慮使用緩存。java
Guava Cache 與 ConcurrentMap 很類似,但也不徹底同樣。最基本的區別是 ConcurrentMap 會一直保存所 有添加的元素,直到顯式地移除。相對地,Guava Cache 爲了限制內存佔用,一般都設定爲自動回收元素。在 某些場景下,儘管 LoadingCache 不回收元素,它也是頗有用的,由於它會自動加載緩存。緩存
一般來講,Guava Cache 適用於:服務器
若是你的場景符合上述的每一條,Guava Cache 就適合你。ide
如同範例代碼展現的同樣,Cache 實例經過 CacheBuilder 生成器模式獲取,可是自定義你的緩存纔是最有趣的部分。工具
注:若是你不須要 Cache 中的特性,使用 ConcurrentHashMap 有更好的內存效率——但 Cache 的大多數特性都很難基於舊有的 ConcurrentMap 複製,甚至根本不可能作到。性能
在使用緩存前,首先問本身一個問題:有沒有合理的默認方法來加載或計算與鍵關聯的值?若是有的話,你應當使用 CacheLoader。若是沒有,或者你想要覆蓋默認的加載運算,同時保留"獲取緩存-若是沒有-則計算"[get-if-absentcompute]的原子語義,你應該在調用 get 時傳入一個 Callable 實例。緩存元素也能夠經過 Cache.put 方法直接插入,但自動加載是首選的,由於它能夠更容易地推斷全部緩存內容的一致性。ui
LoadingCache 是附帶 CacheLoader 構建而成的緩存實現。建立本身的 CacheLoader 一般只須要簡單地實 現 V load(K key) throws Exception 方法。例如,你能夠用下面的代碼構建 LoadingCache:code
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumSize(1000) .build(new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } }); ... try { return graphs.get(key); } catch (ExecutionException e) { throw new OtherException(e.getCause()); }
從 LoadingCache 查詢的正規方式是使用 get(K)方法。這個方法要麼返回已經緩存的值,要麼使用 CacheLoader 向緩存原子地加載新值。因爲 CacheLoader 可能拋出異常,LoadingCache.get(K)也聲明爲拋出 ExecutionException 異常。若是你定義的CacheLoader 沒有聲明任何檢查型異常,則能夠經過 getUnchecked(K)查找緩存;但必須注意,一旦 CacheLoader 聲明瞭檢查型異常,就不能夠調用 getUnchecked(K)。內存
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES) .build(new CacheLoader<Key, Graph>() { public Graph load(Key key) { // 沒有檢查異常 return createExpensiveGraph(key); } }); ... return graphs.getUnchecked(key);
getAll(Iterable<? extends K>)方法用來執行批量查詢。默認狀況下,對每一個不在緩存中的鍵,getAll 方法會單獨調用 CacheLoader.load 來加載緩存項。若是批量的加載比多個單獨加載更高效,你能夠重載 CacheLoader.loadAll 來利用這一點。getAll(Iterable)的性能也會相應提高。ci
注:CacheLoader.loadAll 的實現能夠爲沒有明確請求的鍵加載緩存值。例如,爲某組中的任意鍵計算值時,可以獲取該組中的全部鍵值,loadAll 方法就能夠實現爲在同一時間獲取該組的其餘鍵值。校注:getAll(Iterable<?extends K>)方法會調用 loadAll,但會篩選結果,只會返回請求的鍵值對。
全部類型的 Guava Cache,無論有沒有自動加載功能,都支持 get(K, Callable)方法。這個方法返回緩存中相應的值,或者用給定的 Callable 運算並把結果加入到緩存中。在整個加載方法完成前,緩存項相關的可觀察狀態都不會更改。這個方法簡便地實現了模式"若是有緩存則返回;不然運算、緩存、而後返回"。
Cache<Key, Graph> cache = CacheBuilder.newBuilder().maximumSize(1000).build(); ... try { // 若是密鑰不在 cache.get(key, new Callable<Key, Graph>() { @Override public Value call() throws AnyException { return doThingsTheHardWay(key); } }); } catch (ExecutionException e) { throw new OtherException(e.getCause()); }
使用 cache.put(key, value)方法能夠直接向緩存中插入值,這會直接覆蓋掉給定鍵以前映射的值。使用 Cache.asMap()視圖提供的任何方法也能修改緩存。但請注意,asMap 視圖的任何方法都不能保證緩存項被原子地加載到緩存中。進一步說,asMap 視圖的原子運算在 Guava Cache 的原子加載範疇以外,因此相比於 Cache.asMap().putIfAbsent(K, V),Cache.get(K, Callable) 應該老是優先使用。
LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(new CacheLoader<Object, Object>() { // 若沒有元素,則建立而且放入緩存 @Override public Object load(Object key) throws Exception { return key.hashCode(); } });
LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().initialCapacity(10) // 初始化個數 .maximumSize(55)// 設置最大個數 .build(new CacheLoader<Object, Object>() { // 若沒有元素,則建立而且放入緩存 @Override public Object load(Object key) throws Exception { return key.hashCode(); } });
LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumWeight(1000) // 設置重量,配合weigher使用 .weigher(new Weigher<Object, Object>() { @Override public int weigh(Object key, Object value) { return 100; } }).build(new CacheLoader<Object, Object>() { // 若沒有元素,則建立而且放入緩存 @Override public Object load(Object key) throws Exception { return key.hashCode(); } });