Guava cache 特性及使用

特性

guava cache類似concurrentMap使用分段鎖提高併發寫的效率

  1. 支持緩存三種緩存淘汰策略
  2. 支持緩存移除監聽(移除、過期、gc回收)
  3. 支持key/value的軟引用、弱引用緩存
  4. 命中率統計

加載數據的方式

1.從CacheLoader加載
LoadingCache.get(key)方法返回緩存中的值,如果不存在,自動調用內部CacheLoader.load(key)方法返回value,並更新緩存

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) throws Exception {
               return createExpensiveGraph(key);
             }
           });

...
try {
  return graphs.get(key);
} catch (ExecutionException e) {
  throw new OtherException(e.getCause());
}
 或者
 return graphs.getUncheck(key);

2. 從Callable加載

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

3. 直接插入
cache.put(key, value)

緩存淘汰策略

  1. 基於容量大小淘汰(緩存的條數)
  2. 基於時間淘汰
    • 基於最近訪問淘汰 expireAfterAccess(long, TimeUnit)
    • 基於最近寫淘汰 expireAfterWrite(long, TimeUnit)
    • 刷新 refreshAfterWrite(long, TimeUnit)
  3. 基於引用淘汰
    • value軟引用
    • key弱引用
    • key軟引用
  4. 手動移除
    • 單個key移除 Cache.invalidate(key)
    • segment中移除 Cache.invalidateAll(keys)
    • 全部移除 Cache.invalidateAll()

刷新

優點:阻塞一個請求線程直到加載到新數據,新數據未加載成功會返回老數據
缺點:當刷新時間到的時候,必須有請求才會執行刷新

@GwtIncompatible("Futures")
  public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
    checkNotNull(key);
    checkNotNull(oldValue);
    // reload默認調用load方法,若不重寫則阻塞請求線程,可開啓線程異步加載
    return Futures.immediateFuture(load(key));
  }
// 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;
               }
             }
           });

數據結構

guava cache採用類似jdk1.7中ConcurrentHashMap的實現,類似Segment數組+HashTable,寫操作對單個個Segment加鎖,通過鎖細化提高併發寫的效率。

guava cache 數據結構
AccessQueue 雙向鏈表,新entry會插入到尾部,存在的entry也會更新到尾部,靠近頭部的entry就是最近最少訪問的對象,WriteQueue 同理,靠近頭部的entry表示最近最少更新的對象。因爲AccessQueue是Segment級別的,所以Guava cache的LRU不是全局的,而是Segment內部的LRU

主要類

CacheBuilder:構建者模式,返回Cache或LoadingCache類型的實例
Cache接口:定義緩存操作的方法,插入、獲取、過期、清理等
LocalCache類:核心類
LoadingCache接口:能夠動態加載的get/refresh方法(相對Local這種不變的模式來說是半持久的semi-persistent)
下面兩個類作爲LocalCache的內部類,類似適配器模式,具體處理委託給LocalCache
LocalManualCache類:手動加載模式,通過Callable方式交給調用者實現
LocalLoadingCache類:自動加載模式,通過CacheLoader#load方法實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章