Guava Cache简介
Guava是谷歌提供的一个核心Java类库,其中包括新的集合类型、不可变集合、图库,以及用于并发、I/O、Hash、缓存、字符串等的 实用工具。它在谷歌中的大多数Java项目中被广泛使用,也被许多其他公司广泛使用,熟练掌握这些工具类能帮助我们快速的处理日常开发中的一些问题,比如,不可变集合、集合的转换、字符串处理、本地缓存等
日常开发中的使用场景
在我们日常的开发中,当多次获取同一份数据而数据变化不频繁时,我们可以考虑使用缓存。
这里用到的就是内存和硬盘的性能不对等,我们知道内存的读取速度很快,而硬盘相对来说比较慢
而传统的关系型数据库把数据存储在硬盘上,这样在高并发读写的场景下,就会出现性能瓶颈
这个时候,如果在数据库前面加一层缓存,把数据库里面的热点数据缓存一份到内存中,读取的时候直接从内存中取,这样就可以大大的提升读取的性能
Guava中的缓存实现
Guava中的缓存是本地缓存的实现,与ConcurrentMap相似,但不完全一样。最基本的区别就是,ConcurrentMap会一直保存添加进去的元素,除非你主动remove掉。而Guava Cache为了限制内存的使用,通常都会设置自动回收
Guava Cache的使用场景:
以空间换取时间,就是你愿意用内存的消耗来换取读取性能的提升 你已经预测到某些数据会被频繁的查询 缓存中存放的数据不会超过内存空间
Guava Cache详细介绍:原文链接:https://blog.csdn.net/pzjtian/article/details/106910046
如何使用Guava Cache:
先来看一个简单示例,缓存字符串实例的大小形式。首先,我们创建ChcheLoader,用于计算存储在缓存中的值,然后我们便捷的CacheBuilder类依照规范构建缓存:
@Test
public void whenCacheMiss_thenValueIsComputed() {
CacheLoader loader;
loader = new CacheLoader() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache cache;
cache = CacheBuilder.newBuilder().build(loader);
assertEquals(0, cache.size());
assertEquals("HELLO", cache.getUnchecked("hello"));
assertEquals(1, cache.size());
}
因为“hello” 键对应值在缓存中没有,所以值被计算并缓存。注意,我们使用getUnchecked() 方法,如果对应值不存在,则计算并缓存值到缓存中。
Guava Cache参数配置说明:
//缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
LoadingCache studentCache
//CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
= CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置写缓存后8秒钟过期
.expireAfterWrite(8, TimeUnit.SECONDS)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//设置要统计缓存的命中率
.recordStats()
//设置缓存的移除通知
.removalListener(new RemovalListener
具体使用Guava缓存 java代码:
导入依赖:
com.google.guava
guava
20.0
public class GuavaService {
@Autowired
private UserMapper userMapper;
public LoadingCache> userListCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.maximumSize(2)
.build(
new CacheLoader>() {
@Override
public List load(String key) throws Exception {
return userMapper.queryUserList();
}
}
);
public List getUserList(){
return userListCache.getUnchecked("ALL");
}
}
@RestController
public class GuavaTestController {
@Autowired
GuavaService guavaService;
@RequestMapping("/test1")
public List test1(){
return guavaService.getUserList();
}
}
踩坑记录:在取缓存赋值给List时,直接对原List操作,导致其他地方取这个缓存的时候值发生了变化。
@RequestMapping("/test2")
public List test2(){
List list = guavaService.getUserList();
User user = new User();
user.setName("testguava");
user.setId(5555);
user.setMale("男");
list.add(user);
return list;
}
当调用test2的时候,userListCache这个缓存值会不断增加,当缓存过期后,恢复正常。
正确操作应该是把缓存取出的值重新赋值给List,对新List进行操作,也就是克隆值。
@RequestMapping("/test2")
public List test2(){
List userList = new ArrayList();
List list = guavaService.getUserList();
for (User user:list) {
userList.add(user);
}
User user = new User();
user.setName("testguava");
user.setId(5555);
user.setMale("男");
userList.add(user);
return userList;
}
这里推荐另一种方式进行该操作,使用httpclient包下的 CloneUtils.cloneObject方法进行数据的克隆。
1、导入依赖:
org.apache.httpcomponents
httpclient
4.3.6
2、
import org.apache.http.client.utils.CloneUtils;
3、
List targets = CloneUtils.cloneObject(GuavaService.getUserList());
//然后对targets直接操作就行