cache組件中核心的類和接口列舉以下:
接口:java
抽象類:算法
類:設計模式
RemovalListeners 該類的文檔中說的是A collection of common removal listeners.感受這個類並非這個做用,這個類提供了一個asynchronous(RemovalListener, Executor)的方法,代碼以下:緩存
public static <K, V> RemovalListener<K, V> asynchronous( final RemovalListener<K, V> listener, final Executor executor) { checkNotNull(listener); checkNotNull(executor); return new RemovalListener<K, V>() { @Override public void onRemoval(final RemovalNotification<K, V> notification) { executor.execute( new Runnable() { @Override public void run() { listener.onRemoval(notification); } }); } }; }
看這意思是將監聽器轉成異步執行的,也就是在移除緩存中的數據時,用異步的方法執行onRemoval操做,在onRemoval比較耗時的時候會提高性能,不至於阻塞對緩存的其餘操做。markdown
枚舉類:網絡
下面列出Cache組件的特色:
- 自動加載Entry(key-value對)到緩存中
- 當緩存超過設定的最大大小時採用LRU算法進行緩存的剔除
- 可設定緩存過時時間,基於最後一次訪問或者最後一次寫入緩存兩種方式
- keys自動使用WeakReference進行包裹
- values自動使用WeakReference或者SoftReference進行包裹
- 當Entry從緩存中剔除時會有通知機制
- 可以對緩存的使用狀況進行統計。異步
使用示例:async
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(MY_LISTENER) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } });}
首先調用newBuilder靜態方法產生CacheBuilder對象,而後開始裝配。maximumSize(10000)表示緩存最多存放10000個鍵值對,超過這個數會利用LRU算法進行剔除,expireAfterWrite(10, TimeUnit.MINUTES)表示在key-value對寫入緩存以後10分鐘過時(過時數據不會馬上進行清除,而是在下一次進行get操做的時候判斷是否過時,若過時則剔除)。removalListener(MY_LISTENER)添加監聽器,在進行剔除操做的時候會調用該監聽器的onRemoval方法進行操做。MY_LISTENER表明實現了RemovalListener接口的子類對象。build()方法能夠傳入一個CacheLoader類的子類對象,該對象用來在get數據失敗時進行數據的load操做,load操做的過程就在重寫的load方法中。ide
注:若是不須要自動裝載數據的功能,能夠在最後的build()方法中不穿遞任何參數。帶不帶CacheLoad類型參數的build方法代碼以下所示:性能
public <K1 extends K, V1 extends V> Cache<K1, V1> build() { checkWeightWithWeigher(); checkNonLoadingCache(); return new LocalCache.LocalManualCache<K1, V1>(this); } public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build( CacheLoader<? super K1, V1> loader) { checkWeightWithWeigher(); return new LocalCache.LocalLoadingCache<K1, V1>(this, loader); }
能夠看出來它們返回的Cache實例類型分別是LocalCache.LocalManualCache和LocalCache.LocalLoadingCache這兩個靜態類。再去LocalCache類裏面稍微看一下這兩個靜態內部類的繼承層次。
static class LocalManualCache<K, V> implements Cache<K, V>, Serializable static class LocalLoadingCache<K, V> extends LocalManualCache<K, V> implements LoadingCache<K, V>
能夠看到LocalLoadingCache在繼承自LocalManualCache的基礎上還實現了LoadingCache接口。也就是說該類的一些方法中涉及到自動加載數據到緩存中的功能。所以構建哪一種Cache徹底取決於本身的需求。
最後須要提出的是,還能夠重寫CacheLoader中的loadAll(Iterable keys)方法,該方法能夠用來批量加載數據,在哪一種場景下須要這個方法呢?舉一個例子,好比根據key獲取value的操做須要通過網絡鏈接,比較耗時,則批量導入數據則能夠大大節省時間,也就是發一次網絡請求獲取一批數據回來。若是重寫了loadAll,須要利用批量加載數據,那麼就須要相應地調用Cache實例的getAll(Iterable keys)方法進行數據的批量獲取,批量獲取的過程當中,如有一批key對應的value沒有在緩存中,則會調用該loadAll方法進行批量加載。若沒有重寫loadAll方法,則會依次調用load方法去進行加載,所以是否須要重寫loadAll方法能夠看是否批量加載能大大節省時間。
使用示例以下:
LoadingCache<Key, Graph> loadingCache= CacheBuilder.newBuilder()
.maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(MY_LISTENER) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } public Graph loadAll(Iterable<Key> keys) { return createExpensiveGraphs(keys); } });} //用法: Map<Key, Graph> mygraphs = loadingCache.getAll(keys);
若須要的數據不在緩存中或者已過時,若是重寫了loadAll方法,則getAll方法內部會去調用loadAll方法加載須要的數據到緩存中,若是沒有重寫loadAll方法,getAll內部會依次調用load方法進行數據的加載。見以下代碼:
try { Map<K, V> newEntries = loadAll(keysToLoad, defaultLoader);//該laodAll方法會在defaultLoader.loadAll()方法沒有進行重寫時拋出異常,被下面的catch捕獲。(由於默認的loadAll重寫的邏輯就是是簡單地拋出一個異常。) for (K key : keysToLoad) { V value = newEntries.get(key); if (value == null) { throw new InvalidCacheLoadException("loadAll failed to return a value for " + key); } result.put(key, value); } } catch (UnsupportedLoadingOperationException e) { // loadAll not implemented, fallback to load for (K key : keysToLoad) { misses--; // get will count this miss result.put(key, get(key, defaultLoader));//捕獲到異常說明沒有重寫loadAll方法,則在get方法中會依次調用defaultLoader的load方法進行載入 } }
未完待續。。。。 若有不當之處還望指正!