Guava加载缓存的方式
这里值的是当缓存中没有对应键值的时候,guava会自动获取数据的情况,存在两种方式:
- CacheLoader
- callable callback
CacheLoader
private static CacheLoader<String, String> loader = new CacheLoader<String, String>() {
public String load(String key) throws Exception{
//加载缓存数据的方法,比如从数据库中获取数据
}
};
private static LoadingCache<String, String> guavaCahce = CacheBuilder.newBuilder()
.build(loader);
从LoadingCache查询的正规方式是使用get(K)方法。这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值(通过load(String key) 方法加载,上面的例子抛出了异常)。
private static CacheLoader<String, String> loader = new CacheLoader<String, String>() {
public String load(String key) { //这里没有抛出异常
//加载缓存数据的方法,比如从数据库中获取数据
}
};
//那么就可以用getUnchecked(K)访问缓存数据
由于CacheLoader可能抛出异常,LoadingCache.get(K)也声明抛出ExecutionException异常。如果你定义的CacheLoader没有声明任何检查型异常则可以通过getUnchecked(K)查找缓存;但必须注意,一旦CacheLoader声明了检查型异常,就不可以调用getUnchecked(K)
Callable
这种方式不需要在创建的时候指定load方法,但是需要在get的时候实现一个Callable匿名内部类
Cache<Key, Value> cache = CacheBuilder.newBuilder()
.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());
}
- 所有类型的Guava Cache,不管有没有自动加载功能,都支持get(K, Callable)方法。这个方法返回缓存中相应的值,或者用给定的Callable运算并把结果加入到缓存中 在整个加载方法完成前,缓存项相关的可观察状态都不会更改
缓存回收
-
CacheBuilder.maximumSize(size) :设定缓存的容量
-
expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
-
expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回 收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的
-
通过键值的权重去回收
CacheLoader<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
Weigher<String, String> weighByLength;
weighByLength = new Weigher<String, String>() {
@Override
public int weigh(String key, String value) {
return value.length();
}
};
LoadingCache<String, String> cache;
cache = CacheBuilder.newBuilder()
.maximumWeight(16)
.weigher(weighByLength)
.build(loader);
cache.getUnchecked("first");
cache.getUnchecked("second");
cache.getUnchecked("third");
cache.getUnchecked("last");
System.out.println( cache.asMap());
System.out.println(cache.getIfPresent("first"));
System.out.println( cache.getIfPresent("last"));
基于引用的回收
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:老实说,在网上貌似没怎么看到基于引用的例子,有点一知半解
-
CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收
-
CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。
Cache<String,Object> cache = CacheBuilder.newBuilder()
.maximumSize(2)
.weakValues()
.build();
Object value = new Object();
cache.put("key1",value);
value = new Object();//原对象不再有强引用
System.gc();
System.out.println(cache.getIfPresent("key1"));
- CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
asMap()
-
asMap 视图的原子运算在Guava Cache的原子加载范畴之外,所以相比于Cache.asMap().putIfAbsent(K,V)不是原子的
-
cache.asMap()包含当前所有加载到缓存的项。因此相应地,cache.asMap().keySet()包含当前所有已加载键;
-
asMap().get(key)实质上等同于cache.getIfPresent(key),而且不会引起缓存项的加载。这和Map的语义约定一致。
-
所有读写操作都会重置相关缓存项的访问时间,包括Cache.asMap().get(Object)方法和Cache.asMap().put(K, V)方法,但不包括Cache.asMap().containsKey(Object)方法,也不包括在Cache.asMap()的集合视图上的操作。比如,遍历Cache.asMap().entrySet()不会重置缓存项的读取时间。
显示清除
任何时候,你都可以显式地清除缓存项,而不是等到它被回收:
-
个别清除:Cache.invalidate(key)
-
批量清除:Cache.invalidateAll(keys)
-
清除所有缓存项:Cache.invalidateAll()