guava cache是Google guava中提供的一款輕量級的本地緩存組件,其特色是簡單、輕便、完善、擴展性強,內存管理機制也相對完善。java
1.減小了網絡調用的開銷
2.減小了數據請求的序列化和反序列化算法
guava cache 提供了2種類型:
Cache:建立1個緩存.
LoadingCache:它可以經過CacheLoader自發的加載緩存,當獲取緩存中數據不存在時,會經過CacheLoader的load方法自動加載到緩存中(後面會進步說明)數據庫
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的配置項是同樣的api
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.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.getUnchecked(K key); //不檢查value是否存在
在前文提到過,在構建本地緩存時,咱們應該指定一個最大容量來防止出現內存溢出的狀況。在guava中除了提供基於數量和基於內存容量兩種回收策略外,還提供了基於引用的回收。 緩存
這個回收策略很是簡單,咱們只需指定緩存的最大存儲數量maximumSize便可:服務器
CacheBuilder.newBuilder().maximumSize(100).build(); // 緩存數量上限爲100
在最大容量回收策略中,咱們須要設置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算法對以前的緩存進行回收。
強引用: 強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。 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秒內沒有使用) .expireAfterAccess(3,TimeUnit.SECONDS) //設置過時時間(寫入3秒內過時) .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); } }