简介
Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
通常来说,Guava Cache适用于:
- 你愿意消耗一些内存空间来提升速度。
- 你预料到某些键会被查询一次以上。
- 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
如果你的场景符合上述的每一条,Guava Cache就适合你。
如同范例代码展示的一样,Cache实例通过CacheBuilder生成器模式获取,但是自定义你的缓存才是最有趣的部分。
注:如果你不需要Cache中的特性,使用ConcurrentHashMap有更好的内存效率——但Cache的大多数特性都很难基于旧有的ConcurrentMap复制,甚至根本不可能做到。
Guava Cache的使用示例
使用缓存时,最常遇到的场景需要就是:
"获取缓存-如果没有-则计算"[get-if-absent-compute]的原子语义.
具体含义:
- 从缓存中取。
- 缓存中存在该数据,直接返回;
- 缓存中不存在该数据,从数据源中取。
- 数据源中存在该数据,放入缓存,并返回;
- 数据源中不存在该数据,返回空。
刷新机制
三种基于时间的清理或刷新缓存数据的方式:
expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。
expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收。
refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。
一、定时过期
LoadingCache caches = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(30, TimeUnit.MINUTES)
.build(new CacheLoader() {
@Override
public Object load(String key) throws Exception {
return generateValueByKey(key);
}
});
try {
System.out.println(caches.get("key-zorro"));
} catch (ExecutionException e) {
e.printStackTrace();
}
如代码所示新建了名为caches的一个缓存对象,定义了缓存大小、过期时间及缓存值生成方法。maximumSize定义了缓存的容量大小,当缓存数量即将到达容量上线时,则会进行缓存回收,回收最近没有使用或总体上很少使用的缓存项。需要注意的是在接近这个容量上限时就会发生,所以在定义这个值的时候需要视情况适量地增大一点。
另外通过expireAfterWrite这个方法定义了缓存的过期时间,写入十分钟之后过期。
在build方法里,传入了一个CacheLoader对象,重写了其中的load方法。当获取的缓存值不存在或已过期时,则会调用此load方法,进行缓存值的计算。
二、定时刷新
LoadingCache caches = CacheBuilder.newBuilder()
.maximumSize(100)
.refreshAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader() {
@Override
public Object load(String key) throws Exception {
return generateValueByKey(key);
}
});
try {
System.out.println(caches.get("key-zorro"));
} catch (ExecutionException e) {
e.printStackTrace();
}
每隔十分钟缓存值则会被刷新。
有一个需要注意也很难处理的地方,这里的定时并不是真正意义上的定时。Guava cache的刷新需要依靠用户请求线程,让该线程去进行load方法的调用,所以如果一直没有用户尝试获取该缓存值,则该缓存也并不会刷新。
三、异步刷新
ListeningExecutorService backgroundRefreshPools =
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20));
LoadingCache caches = CacheBuilder.newBuilder()
.maximumSize(100)
.refreshAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader() {
@Override
public Object load(String key) throws Exception {
return generateValueByKey(key);
}
@Override
public ListenableFuture