目录
【缓存】
缓存在很多场景下都是相当有用的。例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管 LoadingCache不回收元素,它也是很有用的,因为它会自动加载缓存。
通常来说,Guava Cache适用于:
● 你愿意消耗一些内存空间来提升速度。
● 你预料到某些键会被查询一次以上。
● 缓存中存放的数据总量不会超出内存容量。
但注意:Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。
private static void CacheTest() throws Exception {
LoadingCache<Integer, Goods> cache = CacheBuilder.newBuilder()
.concurrencyLevel(2) // 设置并发级别,并发级别是指可以同时写缓存的线程数
.expireAfterWrite(10, TimeUnit.MINUTES) // 设置写缓存后过期时间
.expireAfterAccess(10, TimeUnit.SECONDS) // 设置访问后的过期时间
.refreshAfterWrite(10, TimeUnit.SECONDS) // 设置缓存定时刷新时间
.recordStats() // 设置要统计缓存的命中率,通过 status()方法查看
.initialCapacity(10) // 设置缓存容器的初始容量
.weakKeys() // 设置弱引用存储键
// .maximumSize(100) // 设置缓存最大容量为,超过最大容量 按照LRU最近最少使用算法来移除缓存项
.maximumWeight(100) // 设置最大权重
.weigher(new Weigher<Object, Object>() { // 设置权重函数
@Override
public int weigh(Object key, Object value) {
return 100;
}
})
.removalListener(new RemovalListener<Object, Object>() { //设置缓存的移除通知
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
.build(
new CacheLoader<Integer, Goods>() {
@Override
public Goods load(Integer key) throws Exception {
System.out.println("load goods " + key);
return new Goods(key, "name" + key);
}
}
);
for (int i = 0; i < 20; i++) {
System.out.println(cache.get(1));
TimeUnit.SECONDS.sleep(1);
}
System.out.println("cache status: " + cache.stats().toString());
System.out.println("cache map: " + cache.asMap()); // 返回 ConcurrentMap
}
static class Goods {
private int id;
private String name;
public Goods(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Students{id=" + id + ", name='" + name + "'}";
}
}
一、加载
● LoadingCache通过 CacheBuilder创建,只需要实现 load(K key) throws Exception 方法即可自定义缓存。
● 取缓存的通用方法是 get(K)。若缓存不存在,则 CacheLoader会向缓存原子性的加载新值。
如果 CacheLoader没有定义异常,可以通过 getUnchecked(K)查找;但如果声明了异常,就不能使用 getUnchecked(K)。
● getAll(Iterable<? extends K>)方法用来执行批量查询。默认情况下,对每个不存在的缓存都会单独的去调用 load(K)方法。另外,如果批量加载会更高效,可使用 loadAll()方法。
● 使用 put(key, value)方法可以直接向缓存插入值,并且直接覆盖掉给定键之前映射的值。
二、回收
Guava Cache提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收
1.基于容量的回收
- | - |
---|---|
CacheBuilder.maximumSize(long) | 缓存将尝试回收最近没有使用或总体上很少使用的缓存项(发生在缓存项数目逼近限定值时) |
CacheBuilder.weigher(Weigher) | 指定一个权重函数,并用 maximumWeight(long)指定最大总重 |
2.定时回收
- | - |
---|---|
expireAfterAccess(long, TimeUnit) | 缓存项在给定时间内没有被读/写访问,则回收 |
expireAfterWrite(long, TimeUnit) | 缓存项在给定时间内没有被写访问,则回收 |
3.基于引用的回收
- | - |
---|---|
CacheBuilder.weakKeys() | 使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收 |
CacheBuilder.weakValues() | 使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收 |
CacheBuilder.softValues() | 使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收 |
4.显示清除
- | - |
---|---|
invalidate(key) | 单个清除 |
invalidateAll(keys) | 批量清除 |
invalidateAll() | 全部清除 |
5.移除监听器
- | - |
---|---|
CacheBuilder.removalListener(RemovalListener) | 当缓存项被移除时做一些额外操作。 |
6.刷新
- | - |
---|---|
refresh(K) | 刷新,为键加载新值,这个过程可以是异步的 |
CacheBuilder.refreshAfterWrite() | 自动定时刷新 |
三、其他
1.统计
CacheBuilder.recordStats()用于设置缓存的统计功能,通过 status()方法返回统计信息:
● hitRate(): 缓存命中率
● averageLoadPenalty():加载平均时间(ns)
● evictionCount():缓存项被回收的总数,不包括显示清除
● totalLoadTime(): 总加载时间
2.asMap视图
asMap视图提供了缓存的ConcurrentMap形式
关于guava cache的详细解读可以参考:
https://mp.weixin.qq.com/s/vi8Y-u-uZc6V357yqwVH1g