譯 Guava Cache

源:https://github.com/google/guava/wiki/CachesExplained

適用性


緩存及其有用。blala

Cache類似于一個(gè)ConcurrentMap,但并不完全一樣。最基礎(chǔ)的不同是ConcurrentMap保存所有的元素知道它們被明確刪除。而Cache是會(huì)安排配置自動(dòng)刪除元素的以便于限制內(nèi)存占用。在某些用例中,LoadingCache即便不使用它的刪除功能,任然可以利用它的自動(dòng)加載功能。

加載數(shù)據(jù)


使用CacheLoader加載

LoadingCache是一個(gè)附加著CacheLoaderCache。
在創(chuàng)建LoadingCache的時(shí)候傳入CacheLoader的實(shí)現(xiàn),主要是實(shí)現(xiàn)V load(K key) throws Exception方法,既怎么去加載一個(gè)key的值。

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());
}

當(dāng)我們通過get(K)去查詢時(shí),會(huì)返回已經(jīng)被緩存的值,或者使用CacheLoader“原子性” 的加載一個(gè)新的值到緩存。因?yàn)?code>load方法本身會(huì)拋異常,所以get方法也會(huì)拋對(duì)應(yīng)的ExecutionException異常表示load失敗?;蛘咧苯邮褂?code>getUncheked(k)方法,所有異常都會(huì)被包裹成UncheckedException丟出。

當(dāng)使用loadAll加載大量key時(shí),如果遇到key加載,loadAll是迭代去取值的,如果需要更有效率的取法,自己實(shí)現(xiàn)。

使用Callable加載

CacheLoader是創(chuàng)建Cache的時(shí)候的默認(rèn)加載器,get(K,Callable<V>)則是get的時(shí)候單獨(dú)傳入一個(gè)針對(duì)本次調(diào)用的加載邏輯。

Cache<Key, Value> cache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .build(); // look Ma, no CacheLoader
...
try {
  // If the key wasn't in the "easy to compute" group, we need to
  // do things the hard way.
  cache.get(key, new Callable<Value>() {
    @Override
    public Value call() throws AnyException {
      return doThingsTheHardWay(key);
    }
  });
} catch (ExecutionException e) {
  throw new OtherException(e.getCause());
}
直接手動(dòng)插入

cache.put(key, value)

刪除數(shù)據(jù)


根據(jù)容量刪除

如果對(duì)Cache的大小在意。創(chuàng)建的時(shí)候配置CacheBuilder.maxmumSize(long)配置緩存大小。
當(dāng)"接近容量"大小的時(shí)候會(huì)自動(dòng)刪除元素。元素也可以有Weight比重,但注意是在元素創(chuàng)建的時(shí)候計(jì)算出來靜態(tài)放置的。后面不會(huì)改變。

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumWeight(100000)
       .weigher(new Weigher<Key, Graph>() {
          public int weigh(Key k, Graph g) {
            return g.vertices().size();
          }
        })
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) { // no checked exception
               return createExpensiveGraph(key);
             }
           });
根據(jù)時(shí)間刪除

2種策略,見名知意:
expireAfterAccess(long, TimeUnit)
expireAfterWrite(long, TimeUnit)
guava提供測(cè)試工具模擬時(shí)間流逝TickerCacheBuilder.ticker(Ticker)

根據(jù)引用刪除

最好使用配置明顯的Cache size去解決內(nèi)存大小,用折后在哪個(gè)策略,是依賴于JVM,而且會(huì)導(dǎo)致緩存使用==去代替'compare`在內(nèi)部邏輯中的比較。因?yàn)閖vm的判斷是基于引用的。

手動(dòng)刪除

Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll()

刪除的時(shí)候得到通知Removal Listeners

不要進(jìn)行耗性能和超時(shí)的動(dòng)作,如果需要,考慮RemovalListeners.asynchronous(RemovalListener,Executor)包裹。

刪除數(shù)據(jù)的時(shí)候到底發(fā)生了什么?

延遲刪除,只在讀和寫的時(shí)候才真正去做刪除維護(hù),所以remove listener才不能過長,因?yàn)槭峭綀?zhí)行的,會(huì)阻塞對(duì)應(yīng)的操作。需要頻繁刪除維護(hù),需要用戶自己去調(diào)用cleanUp().

刷新數(shù)據(jù)


LoadingCahe.refresh(K) 異步調(diào)用底層的CacheLoader.reload(K,V)刷新值。如果異常,保留原來的值,只打印日志。
自動(dòng)定時(shí)刷新可以在CacheBuilder中配置CacheBuilder.refreshAfterWrite(long,TimeUnit).
值得注意的是refreshAfterWrite僅僅只在到時(shí)后,標(biāo)記某個(gè)key可以被更新了,只有在查詢來臨的時(shí)候才會(huì)真正觸發(fā)更新。在同時(shí)有定時(shí)過期和定時(shí)更新的Cache上,只有在標(biāo)記的key在更新時(shí)間到后,查詢沒有來臨,更新未觸發(fā)。才會(huì)被過期策略考慮入刪除。

注意Guava提供的demo,CacheLoader.reload的實(shí)現(xiàn)不僅可以判斷哪些可以key應(yīng)該被更新,而且是異步的。

// Some keys don't need refreshing, and we want refreshes to be done asynchronously.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .refreshAfterWrite(1, TimeUnit.MINUTES)
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) { // no checked exception
               return getGraphFromDatabase(key);
             }

             public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {
               if (neverNeedsRefresh(key)) {
                 return Futures.immediateFuture(prevGraph);
               } else {
                 // asynchronous!
                 ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
                   public Graph call() {
                     return getGraphFromDatabase(key);
                   }
                 });
                 executor.execute(task);
                 return task;
               }
             }
           });

統(tǒng)計(jì)功能


CacheBuilder.recordStats 開啟緩存的輔助統(tǒng)計(jì)功能。
Cache.stats方法可以返回一個(gè)CacheStats對(duì)象,包含很多統(tǒng)計(jì)信息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容