译 Guava Cache

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

适用性


缓存及其有用。blala

Cache类似于一个ConcurrentMap,但并不完全一样。最基础的不同是ConcurrentMap保存所有的元素知道它们被明确删除。而Cache是会安排配置自动删除元素的以便于限制内存占用。在某些用例中,LoadingCache即便不使用它的删除功能,任然可以利用它的自动加载功能。

加载数据


使用CacheLoader加载

LoadingCache是一个附加着CacheLoaderCache
在创建LoadingCache的时候传入CacheLoader的实现,主要是实现V load(K key) throws Exception方法,既怎么去加载一个key的值。

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

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

当我们通过get(K)去查询时,会返回已经被缓存的值,或者使用CacheLoader“原子性” 的加载一个新的值到缓存。因为load方法本身会抛异常,所以get方法也会抛对应的ExecutionException异常表示load失败。或者直接使用getUncheked(k)方法,所有异常都会被包裹成UncheckedException丢出。

当使用loadAll加载大量key时,如果遇到key加载,loadAll是迭代去取值的,如果需要更有效率的取法,自己实现。

使用Callable加载

CacheLoader是创建Cache的时候的默认加载器,get(K,Callable)则是get的时候单独传入一个针对本次调用的加载逻辑。

Cache 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() {
    @Override
    public Value call() throws AnyException {
      return doThingsTheHardWay(key);
    }
  });
} catch (ExecutionException e) {
  throw new OtherException(e.getCause());
}
直接手动插入

cache.put(key, value)

删除数据


根据容量删除

如果对Cache的大小在意。创建的时候配置CacheBuilder.maxmumSize(long)配置缓存大小。
"接近容量"大小的时候会自动删除元素。元素也可以有Weight比重,但注意是在元素创建的时候计算出来静态放置的。后面不会改变。

LoadingCache graphs = CacheBuilder.newBuilder()
       .maximumWeight(100000)
       .weigher(new Weigher() {
          public int weigh(Key k, Graph g) {
            return g.vertices().size();
          }
        })
       .build(
           new CacheLoader() {
             public Graph load(Key key) { // no checked exception
               return createExpensiveGraph(key);
             }
           });
根据时间删除

2种策略,见名知意:
expireAfterAccess(long, TimeUnit)
expireAfterWrite(long, TimeUnit)
guava提供测试工具模拟时间流逝TickerCacheBuilder.ticker(Ticker)

根据引用删除

最好使用配置明显的Cache size去解决内存大小,用折后在哪个策略,是依赖于JVM,而且会导致缓存使用==去代替'compare`在内部逻辑中的比较。因为jvm的判断是基于引用的。

手动删除

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

删除的时候得到通知Removal Listeners

不要进行耗性能和超时的动作,如果需要,考虑RemovalListeners.asynchronous(RemovalListener,Executor)包裹。

删除数据的时候到底发生了什么?

延迟删除,只在读和写的时候才真正去做删除维护,所以remove listener才不能过长,因为是同步执行的,会阻塞对应的操作。需要频繁删除维护,需要用户自己去调用cleanUp().

刷新数据


LoadingCahe.refresh(K) 异步调用底层的CacheLoader.reload(K,V)刷新值。如果异常,保留原来的值,只打印日志。
自动定时刷新可以在CacheBuilder中配置CacheBuilder.refreshAfterWrite(long,TimeUnit).
值得注意的是refreshAfterWrite仅仅只在到时后,标记某个key可以被更新了,只有在查询来临的时候才会真正触发更新。在同时有定时过期和定时更新的Cache上,只有在标记的key在更新时间到后,查询没有来临,更新未触发。才会被过期策略考虑入删除。

注意Guava提供的demo,CacheLoader.reload的实现不仅可以判断哪些可以key应该被更新,而且是异步的。

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

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

统计功能


CacheBuilder.recordStats 开启缓存的辅助统计功能。
Cache.stats方法可以返回一个CacheStats对象,包含很多统计信息。

你可能感兴趣的:(译 Guava Cache)