Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
通常来说,Guava Cache适用于:
你愿意消耗一些内存空间来提升速度。
你预料到某些键会被查询一次以上。
缓存中存放的数据总量不会超出内存 容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
我们使用的Cache一般都会有自动加载的功能
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());
}
从Loadingcache中查询的正规方法就是使用get(key)方法,这个方法要不就是返回已经缓存的值,要使用CacheLoader向缓存原子性的增加新值,由于CacheLoader可能会抛出异常,所以我们往往需要在get()try catch来捕获异常。
getAll(Iterable extends K>)方法用来执行批量查询。默认情况下,对每个不在缓存中的键,getAll方法会单独调用CacheLoader.load来加载缓存项。如果批量的加载比多个单独加载更高效,你可以重载CacheLoader.loadAll来利用这一点。getAll(Iterable)的性能也会相应提升。
而且Cache支持,在get()方法中加入CallAble这个方法会返回缓存中的值,或者用给定的Callable来计算并把结果放入到缓存中
使用cache.put(key, value)方法可以直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值。使用Cache.asMap()视图提供的任何方法也能修改缓存。但请注意,asMap视图的任何方法都不能保证缓存项被原子地加载到缓存中。
进一步说,asMap视图的原子运算在Guava Cache的原子加载范畴之外,所以相比于Cache.asMap().putIfAbsent(K,V),
Cache.get(K, Callable) 应该总是优先使用, 因为Gava保证了get()是原子性的更新缓存信息
如果需要缓存项的数目不超过固定值,只需使用CacheBuilder.maximumSize(long)
这种是简单的回收最近没有使用或者总体上使用很少的缓存项
另外使用权重值回收也是不错的选择
LoadingCache cache1 = CacheBuilder.newBuilder().maximumWeight(3).weigher((String key, String value) -> {
return key.length() + value.length();
}).build(new CacheLoader() {
@Override
public String load(String key) throws Exception {
return new Random(System.currentTimeMillis()).nextInt(100) + key;
}
});
CacheBuilder提供了两种定时回收的方法,
expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样
expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:
CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用键的缓存用而不是equals比较键。
CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用值的缓存用而不是equals比较值。
CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
通过CacheBuilder.removeListener(RemoveListener)可以声明一个监听器,以便缓存项被移除时做一些额外操作。缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification],其中包含移除原因[RemovalCause]、键和值。
下面是一段使用监听器,代码
@Test
public void testCacheListen() throws ExecutionException, InterruptedException {
CacheLoader loader = new CacheLoader () {
public String load(String key) throws Exception {
return "sailong" + new Random().nextInt(100);
}
};
RemovalListener removalListener = new RemovalListener() {
public void onRemoval(RemovalNotification removal) {
String key = removal.getKey();
String value = removal.getValue();
System.out.println("移除了" + key + ": " + value);
}
};
LoadingCache cache2 = CacheBuilder.newBuilder()
.expireAfterAccess(2, TimeUnit.SECONDS)
.removalListener(removalListener)
.build(loader);
System.out.println(cache2.get("haha"));
TimeUnit.SECONDS.sleep(3);
System.out.println(cache2.get("haha"));
}