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<String, String> loader;
loader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
return key.toUpperCase();
}
};
LoadingCache<String, String> 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<Integer,Student> 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<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(
new CacheLoader<Integer, Student>() {
@Override
public Student load(Integer key) throws Exception {
System.out.println("load student " + key);
Student student = new Student();
student.setId(key);
student.setName("name " + key);
return student;
}
}
);
具体使用Guava缓存 java代码:
导入依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
public class GuavaService {
@Autowired
private UserMapper userMapper;
public LoadingCache<String, List<User>> userListCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.maximumSize(2)
.build(
new CacheLoader<String, List<User>>() {
@Override
public List<User> load(String key) throws Exception {
return userMapper.queryUserList();
}
}
);
public List<User> getUserList(){
return userListCache.getUnchecked("ALL");
}
}
@RestController
public class GuavaTestController {
@Autowired
GuavaService guavaService;
@RequestMapping("/test1")
public List<User> test1(){
return guavaService.getUserList();
}
}
踩坑记录:在取缓存赋值给List时,直接对原List操作,导致其他地方取这个缓存的时候值发生了变化。
@RequestMapping("/test2")
public List<User> test2(){
List<User> list = guavaService.getUserList();
User user = new User();
user.setName("testguava");
user.setId(5555);
user.setMale("男");
list.add(user);
return list;
}
当调用test2的时候,userListCache这个缓存值会不断增加,当缓存过期后,恢复正常。
image-20200703163215480.pngimage-20200703163259110.png
正确操作应该是把缓存取出的值重新赋值给List,对新List进行操作,也就是克隆值。
@RequestMapping("/test2")
public List<User> test2(){
List<User> userList = new ArrayList<User>();
List<User> 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、导入依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
2、
import org.apache.http.client.utils.CloneUtils;
3、
List<User> targets = CloneUtils.cloneObject(GuavaService.getUserList());
//然后对targets直接操作就行
网友评论