一.guava cache 介紹
1.介紹
guava cache是Google guava中提供的一款輕量級的本地緩存組件,其特點是簡單、輕便、完善、擴展性強,內存管理機制也相對完善。
2.使用緩存的優點
1.減少了網絡調用的開銷
2.減少了數據請求的序列化和反序列化
二.guava cache分類
guava cache 提供了2種類型:
Cache:創建1個緩存.
LoadingCache:它能夠通過CacheLoader自發的加載緩存,當獲取緩存中數據不存在時,會通過CacheLoader的load方法自動加載到緩存中(后面會進步說明)
三.Cache的創建
Guava的緩存有許多配置選項,所以為了簡化緩存的創建過程,使用了Builder設計模式,而Builder使用的是鏈式編程的思想,也就是每次調用方法后返回的是對象本生,這樣可以極大的簡化配置過程。
Guava的緩存創建需要通過CacheBuilder的build()方法構建,它的每個方法都返回CacheBuilder本身,直到build方法被調用才會創建Cache或者LoadingCache。
創建過程(這里只做一個簡單的創建,后面會加上各種配置項)
Cache<String, String> cache = CacheBuilder.newBuilder().build(); LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { // 緩存加載邏輯,可以通過查詢數據庫,獲取經常訪問且固定不變的數據 return null; } });
//LoadingCache在創建時需要我們添加一段緩存獲取的邏輯,當我們從緩存中獲取某個key對應的value時,如果緩存中沒有,則會通過load(key)這個方法重新去加載這個value,當獲取到value緩存並返回.
四.構建時緩存配置項的配置
Cache和LoadingCache的配置項是一樣的
concurrencyLevel(int concurrencyLevel) : 設置並發級別 //cache提供了設置並發級別的api,使得緩存支持並發的寫入和讀取。同ConcurrentHashMap類似Guava cache的並發也是通過分離鎖實現。在一般情況下,將並發級別設置為服務器cpu核心數是一個比較不錯的選擇。 initialCapacity(int initialCapacity):設置初始容量 //我們在構建緩存時可以為緩存設置一個合理大小初始容量。由於Guava的緩存使用了分離鎖的機制,擴容的代價非常昂貴。所以合理的初始容量能夠減少緩存容器的擴容次數。 maximumSize(long maximumSize):設置最大存儲量 //Guava Cache可以在構建緩存對象時指定緩存所能夠存儲的最大記錄數量。 //當Cache中的記錄數量達到最大值后再調用put方法向其中添加對象,Guava會先從當前緩存的對象記錄中選擇一條刪除掉,騰出空間后再將新的對象存儲到Cache中。 expireAfterWrite(long duration, TimeUnit unit):設置寫入多久的過期時間 expireAfterAccess(long duration, TimeUnit unit):設置多久沒被訪問(讀/寫)的過期時間 //在構建Cache對象時,可以通過CacheBuilder類的expireAfterAccess和expireAfterWrite兩個方法為緩存中的對象指定過期時間,過期的對象將會被緩存自動刪除。 //其中,expireAfterWrite方法指定對象被寫入到緩存后多久過期,expireAfterAccess指定對象多久沒有被訪問后過期。 //可以同時用expireAfterAccess和expireAfterWrite方法指定過期時間,這時只要對象滿足兩者中的一個條件就會被自動過期刪除。(有等驗證)
//一共4種,這里介紹2種,只不過是參數類型傳的不同而已
removalListener(new RemovalListener<K, V>):設置移除監聽器 //可以為Cache對象添加一個移除監聽器,這樣當有緩存被刪除時可以感知到這個事件。在RemovalListener寫的是刪除回調時的通知邏輯 recordStats():打開統計信息開關 //可以對Cache的命中率、加載數據時間等信息進行統計。 //在構建Cache對象時,可以通過CacheBuilder的recordStats方法開啟統計信息的開關。開關開啟后Cache會自動對緩存的各種操作進行統計,調用Cache的stats方法可以查看統計后的信息。 weakKeys(): //使用弱引用存儲鍵 weakValues()://使用弱引用存儲值 softValues()://使用軟引用存儲值 //這里的配置項會在緩存回收處講解
五.Cache的API操作
cache.asMap(); //將緩存轉換成1個ConcurrentMap
cache.cleanUp(); //清空緩存
cache.get(K key, Callable<? extends V> loader) throws ExecutionException //獲取緩存,當緩存不存在時,則通Callable進行加載並返回。該操作是原子,會拋出ExecutionException異常
cache.getAllPresent(Iterable<?> keys); //通過已存在的keys集合獲取到一個固定長度的map集合
cache.getIfPresent(Object key); //獲取一個緩存,如果該緩存不存在則返回一個null值
cache.invalidate(Object key); //通過key使value無效
cache.invalidateAll(); //使所有的value無效
cache.invalidateAll(Iterable<?> keys); //使keys集合中對應的value無效
cache.put(String key, Object value); //向緩存中添加數據
cache.putAll(Map<? extends K, ? extends V> m); //向級存中添加Map集合
cache.size(); //緩存大小
cache.stats(); //查看緩存命中結果
六.LoadingCache的API操作
loadingCache.getUnchecked(K key); //不檢查value是否存在
七.緩存的回收
在前文提到過,在構建本地緩存時,我們應該指定一個最大容量來防止出現內存溢出的情況。在guava中除了提供基於數量和基於內存容量兩種回收策略外,還提供了基於引用的回收。
1.基於數量的回收
這個回收策略非常簡單,我們只需指定緩存的最大存儲數量maximumSize即可:
CacheBuilder.newBuilder().maximumSize(100).build(); // 緩存數量上限為100
2.基於最大容量的回收
在最大容量回收策略中,我們需要設置2個必要參數:
maximumWeigh:用於指定最大容量
Weigher:在加載緩存時用於計算緩存容量大小。
這里我們例舉一個key和value都是String類型緩存:
CacheBuilder.newBuilder() .maximumWeight(1024 * 1024 * 1024) // 設置最大容量為 1M // 設置用來計算緩存容量的Weigher .weigher(new Weigher<String, String>() { @Override public int weigh(String key, String value) { return key.getBytes().length + value.getBytes().length; } }).build();
//當緩存的最大數量/容量逼近或超過我們所設置的最大值時,Guava就會使用LRU算法對之前的緩存進行回收。
3.基於引用的回收策略
強引用: 強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。 Object o=new Object(); // 強引用 當內存空間不足,垃圾回收器不會自動回收一個被引用的強引用對象,而是會直接拋出OutOfMemoryError錯誤,使程序異常終止。 軟引用: 相對於強引用,軟引用是一種不穩定的引用方式,如果一個對象具有軟引用,當內存充足時,GC不會主動回收軟引用對象,而當內存不足時軟引用對象就會被回收。 SoftReference<Object> softRef=new SoftReference<Object>(new Object()); // 軟引用 Object object = softRef.get(); // 獲取軟引用 使用軟引用能防止內存泄露,增強程序的健壯性。但是一定要做好null檢測。 弱引用: 弱引用是一種比軟引用更不穩定的引用方式,因為無論內存是否充足,弱引用對象都有可能被回收。 WeakReference<Object> weakRef = new WeakReference<Object>(new Object()); // 弱引用 Object obj = weakRef.get(); // 獲取弱引用 guava采用可以配置弱引用和軟引用的策略來讓用戶自行決定緩存數據的類型,這樣可以防止發生內存泄露的現象
八.代碼實現
package com.study; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Test; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; import com.google.common.cache.Cache;; /** * guava cache 測試 * * @author pengbo.zhao * @data 2019年11月5日 上午10:24:15 * * * cache的創建 * * {@link #createCache()} 創建一個簡單的cache * * * */ public class GuavaCache { @Test public void createCache() throws ExecutionException{ Cache<String,String> cache = CacheBuilder.newBuilder() //設置並發數(以獲取當前操作系統cpu數來確定並發數) .concurrencyLevel(Runtime.getRuntime().availableProcessors()) //設置初始容量 .initialCapacity(1000) //設置最大存儲量 .maximumSize(900) //設置過期時間(3秒內沒有使用)在指定時間內沒有進行讀寫,會移除key,下次取的時候從loading中取 .expireAfterAccess(3,TimeUnit.SECONDS) //設置過期時間(寫入3秒內過期)在一定時間內沒有創建/覆蓋時,會移除key,下次從loading中取 .expireAfterWrite(3, TimeUnit.SECONDS) //設置引用清除(設置弱引用存儲值) .weakValues() //設置統計信息 .recordStats() //設置移除通知 .removalListener(new RemovalListener<String, String>() { @Override public void onRemoval(RemovalNotification<String, String> notification) { System.out.println(notification.getKey()+"-"+notification.getValue()+" is remove"); } }) //構建 .build(); cache.put("key1", "value1"); System.out.println(cache.getIfPresent("key1")); String key2 = cache.get("key2",new Callable<String>() { @Override public String call() throws Exception { return "value2"; } }); System.out.println(key2); } }
是在指定項在一定時間內沒有創建/覆蓋時,會移除該key,下次取的時候從loading中取