学习使用Guava Cache
官方文档:https://github.com/google/guava/wiki/CachesExplained
目录
二、快速入门
2.1、引入依赖
2.2、第一个示例
2.3、批量操作
三、拓展
3.1、移除监听器
3.2、刷新缓存
3.3、自定义刷新的操作
一、guava cache介绍
对于常见的cache(缓存),比如memecache、redis、tair这些中间件,也有jdk的一些类库可以当做缓存使用,比如实现了ConcurrentMap接口的ConcurrentHashMap。
ConcurrentHashMap虽然可以让我们在程序中缓存一些数据,但是这些数据其实永远不会过期,除非手动删除,否则数据将一直存在于内存中。
而guava cache(LocalCache,本地缓存),也是实现了ConcurrentMap的类,但他和ConcurrentHashMap的区别在于,guava cache可以设置数据过期以及淘汰机制,更加符合一般的应用需求。
guava cache,是一个本地缓存,也就是说,他的数据是存放在机器内存,这个机器,就是JVM所在的机器(会占用一部分内存),而不是像redis那样专门做缓存的机器。
二、快速入门
2.1、引入依赖
guava cahce是guava的子模块。
guava仓库地址:https://mvnrepository.com/artifact/com.google.guava/guava
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
2.2、第一个示例
下面是一个简单的示例:
package cn.ganlixin.guava; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; public class UseCache { @Test
public void testLoadingCache() throws ExecutionException {
// 创建缓存(容器)
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
// 数据写入1分钟后失效
.expireAfterWrite(1, TimeUnit.MINUTES)
// 支持缓存项的数据最大为3个
.maximumSize(3)
// 实现CacheLoader,当在缓存中未找到所需的缓存项时,会执行CacheLoader的load方法加载缓存。
.build(new CacheLoader<String, String>() {
// 方法的返回值会被缓存,注意,返回null时,会抛异常
@Override
public String load(String key) throws Exception {
System.out.println("key:" + key + " 未找到,开始加载....");
return key + "-" + key;
}
}); // 加入缓存
cache.put("Java", "spring");
cache.put("PHP", "swoole"); // 从缓存中获取值
String res1 = cache.get("Java"); // get方法需要抛出ExecutionException异常
// cache.getUnchecked("Java"); // 功能和get一样,但是不会throw异常
System.out.println(res1);
// 输出:spring // 缓存中为存放key为Golang的缓存项,所以进行加载
String res2 = cache.get("Golang");
System.out.println(res2);
// key:Golang 未找到,开始加载....
// Golang-Golang // Golang的缓存项前面已经加载过了,且没有被清除,所以这次直接获取到对应的value
String res3 = cache.get("Golang");
System.out.println(res3);
// Golang-Golang // 前面添加了3个缓存项(已经达到设置上限,此时再添加缓存项,将会触发淘汰)
cache.put("Node", "KOA");
System.out.println(cache.get("PHP")); // PHP在cache中已被淘汰
// key:PHP 未找到,开始加载....
// PHP-PHP // 查询缓存,如果未找到,不会触发加载操作,而是范围null。
String res4 = cache.getIfPresent("key");
System.out.println(res4); // null
}
}
2.3、批量操作
guava cache支持批量添加、查询、清除操作。
package cn.ganlixin.guava; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import org.junit.Test; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; public class UseCache { @Test
public void testMultiple() throws ExecutionException {
// 创建缓存
LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
.maximumSize(2) // 最大缓存项数量为2
.expireAfterWrite(10, TimeUnit.SECONDS) // 数据写入10秒过期
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
System.out.println("key:" + key + " 未找到,开始加载....");
return key + "-" + key;
}
}); Map<String, String> map = new HashMap<>();
map.put("one", "111111");
map.put("two", "222222");
map.put("three", "3333333");
// 批量添加
loadingCache.putAll(map); List<String> keys = new ArrayList<>();
keys.add("one");
keys.add("two");
// 批量获取
ImmutableMap<String, String> allData = loadingCache.getAll(keys);
System.out.println(allData);
// {one=one-one, two=222222} // 批量清除
loadingCache.invalidateAll(keys);
loadingCache.invalidateAll(); // 全量清除
}
}
三、拓展
3.1、移除监听器
移除监听器,是指监听缓存项,当缓存项被清除时,执行指定对操作。
package cn.ganlixin.guava; import com.google.common.cache.*;
import org.junit.Test; import java.util.concurrent.TimeUnit; public class UseCache { @Test
public void testRemoveListender() { LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(2)
.expireAfterWrite(1, TimeUnit.MINUTES)
// 绑定"移除监听器",当元素被清除时,执行onRemoval方法
.removalListener(new RemovalListener<String, String>() {
@Override
public void onRemoval(RemovalNotification removalNotification) {
Object key = removalNotification.getKey();
Object val = removalNotification.getValue();
System.out.println("被清除的元素,key:" + key + ", val:" + val); }
})
// 当在缓存中未找到key时,执行load操作。
.build(new CacheLoader<String, String>() {
// 当key未找到时,执行的加载操作
@Override
public String load(String key) throws Exception {
System.out.println("未找到key:" + key + ",开始加载...");
return key + key;
}
}); cache.put("one", "111111");
cache.put("two", "123456"); // 继续添加元素,会触发清理操作,触发移除监听器
cache.put("three", "2233333");
// 输出:被清除的元素,key:one, val:111111 String res = cache.getUnchecked("four");
System.out.println(res);
// 输出
// 未找到key:four,开始加载...
// 被清除的元素,key:two, val:123456
// fourfour
}
}
3.2、刷新缓存
刷新缓存,是指一个缓存项写入缓存后,经过一段时间(设置的刷新间隔),再次访问该缓存项的时候,会刷新该缓存项,可以理解为将该项的key取出,执行CacheLoader的load方法,然后将返回值替换旧的值。
可以在创建缓存的时候,设置刷新缓存的时间:
package cn.ganlixin.guava; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; public class UseCache { @Test
public void testRefresh() throws InterruptedException, ExecutionException {
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(2)
// 设置刷新的时机
.refreshAfterWrite(2, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
System.out.println("key:" + key + " 未找到,开始加载....");
return key + "-" + key;
}
}); cache.put("one", "11111");
cache.put("two", "22222"); // 休眠3秒
Thread.sleep(3000L); System.out.println(cache.get("one"));
//key:one 未找到,开始加载....
//one-one System.out.println(cache.get("two"));
//key:two 未找到,开始加载....
//two-two
}
}
需要注意的是,以上面代码为例,并不是设置写入2秒后,就会被刷新,而是当写入2秒后,且再次被访问时,才会被刷新;如果一个缓存项写入的时间超过2秒,但是一直没有访问该项,那么这一项是不会被刷新的。这与Memecache和Redis的原理类似。
3.3、自定义刷新缓存的操作
前面内容中,提到可以使用设置refreshAfterWrite()设置数据写入多久后,再次被访问时,会被刷新,其实,我们可以对于刷新操作自定义,只需要重写CacheLoader的reload方法即可。
package cn.ganlixin.guava; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Test; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; public class UseCache { @Test
public void testReload() throws InterruptedException {
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(2)
// 设置刷新的时机
.refreshAfterWrite(2, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
System.out.println("key:" + key + " 未找到,开始加载....");
return key + "-" + key;
} // 刷新缓存时,执行的操作
@Override
public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
System.out.println("刷新缓存项,key:" + key + ", oldValue:" + oldValue);
return super.reload(key, oldValue);
}
}); cache.put("hello", "world");
Thread.sleep(3000L); System.out.println(cache.getUnchecked("hello"));
// 刷新缓存项,key:hello, oldValue:world
// key:hello 未找到,开始加载....
// hello-hello
}
}
最新文章
- sqlplus 设置
- Linux经常用到的命令
- eclipse 弹出智能提示、代码自动换行
- [转] doxygen使用总结
- 查看文件系统类型的Linux命令
- HDU 1051 - Rightmost Digit
- Codeforces Round #258 (Div. 2) 小结
- extjs 时间可选择时分
- Laravel 数据插入
- ThinkPHP搭建商城一,环境部署
- swift之函数式编程(二)
- Linux(Ubuntu)使用日记------trash-cli防止误删文件
- vue webpack打包后 iconfont引入路径不对
- CSS之表格边框合并、兄弟标签外边距合并、父子标签的外边距合并
- 【原创】大数据基础之Kerberos(2)hive impala hdfs访问
- 一个有趣的异步时序逻辑电路设计实例 ——MFM调制模块设计笔记
- 生产环境rails console spring自动启动的问题
- 团队Alpha冲刺(五)
- boost tss.hpp源码分析
- 将BT转为磁力链接
热门文章
- 定义变量|dirname|basename|printf
- WOW.js——在页面滚动时展现动感的元素动画效果
- 推荐系统之矩阵分解(MF)
- iOS电商常见动画与布局、微信悬浮窗、音乐播放器、歌词解析、拖动视图等源码
- Linux SSH 使用密钥登陆
- python语法基础-基础-变量和数据类型
- 方差分析|残差|MSA/MSE|Completely randomized design|Randomized block design|LSD|主效应|intercept|多重比较|
- springboot学习笔记:5.spring mvc(含FreeMarker+layui整合)
- vue项目中net::ERR_CONNECTION_TIMED_OUT错误
- codeigniter注意点