- 引入依赖
- 常用API
2.1 创建缓存
-2.1.1 创建LoadingCache缓存
-2.1.2 创建Cache缓存
2.2 创建缓存时的API
-2.2.1 基于大小的数据移除
-2.2.2 基于时间的数据移除
-2.2.3 基于引用的回收
-2.2.4 removalListener移除缓存时的监听
-2.2.5 recordStats开启统计功能
2.3 操作缓存的API
-2.3.1 显式移除缓存
-2.3.2 维护缓存的API
官方文档
1. 引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
2. 常用API
2.1 创建缓存
2.1.1 创建LoadingCache缓存
public class CacheTest {
/**
* 当进行查询时,若缓存中无数据,那么使用load方法进行动态加载。
* 若load(key)方法返回"",那么缓存的value=""。
*/
private static LoadingCache<String, String> cache1 = CacheBuilder.
newBuilder().
maximumSize(2). //LRU缓存的最大个数
removalListener((notification) -> System.out.println("移除掉的缓存值" + //移除缓存的回调方法
notification.getCause().name() + ":" +
notification.getKey() + ":" +
notification.getValue())).
build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception { //动态加载缓存
//从SQL或者NoSql 获取对象
if ("a".equals(key)) {
return "a-v";
}
return "";
}
});
public static void main(String[] args) throws Exception {
cache1();
}
private static void cache1() throws ExecutionException {
System.out.println("a的缓存:" + cache1.get("a"));
System.out.println("b的缓存:" + cache1.get("b"));
cache1.put("c", "c");
System.out.println("c的缓存:" + cache1.get("c"));
}
}
执行结果:
a的缓存:a-v
b的缓存:
移除掉的缓存值SIZE:a:a-v
c的缓存:c
当get("b")
,因为cache1中并没有缓存,所以会调用CacheLoader
的load方法加载缓存,注意CacheLoader
的load方法不能返回null。但是返回""
后,缓存存储的值如下图2.1-1所示,也是"'。
2.1.2 创建Cache缓存
@Slf4j
public class CacheTest {
private static Cache<String, String> cache2 = CacheBuilder.newBuilder().
maximumSize(10).build();
private static void cache2() throws Exception {
System.out.println("cache2.getIfPresent(a):" + cache2.getIfPresent("a"));
System.out.println("cache2.get(a) one:" + cache2.get("a", () -> {
log.info("开始维护缓存,key:{}", "a");
return "a-v";
}));
System.out.println("cache2.get(a) two:" + cache2.get("a", () -> {
log.info("开始维护缓存,key:{}", "a");
return "a-v";
}));
cache2.invalidate("a");
System.out.println("invalidate后cache2.getIfPresent(a):" + cache2.getIfPresent("a"));
//填充值
cache2.put("b", "b");
//删除值
System.out.println("put后cache2.getIfPresent(b):" + cache2.getIfPresent("b"));
}
}
执行结果:
cache2.getIfPresent(a):null
10:45:22.869 [main] INFO com.tellme.Test.jdk.CacheTest - 开始维护缓存,key:a
cache2.get(a) one:a-v
cache2.get(a) two:a-v
invalidate后cache2.getIfPresent(a):null
put后cache2.getIfPresent(b):b
Cache缓存,可以看做功能更加强大ConcurrentHashMap
缓存。
2.2 创建缓存时的API
2.2.1 基于大小的数据移除
API方法 | 作用 |
---|---|
maximumSize | 最大的缓存条数,当达到最大条数,使用LRU移除。 |
maximumWeight&&weigher | 最大缓存“重量”,weigher为每一个缓存设置重量,当达到最大重量,使用LRU移除。 |
maximumWeight也不是基于对象真正占用内存,而是通过weigher为每一个缓存定义重量实现的。
private static Cache<String, String> cache = CacheBuilder.newBuilder().
maximumWeight(6).
weigher((k, v) -> {
if ("a".equals(k)) {
return 5;
} else {
return 1;
}
}).
build();
例如上述代码:key=“a”时,可能缓存的value占用内存比较大,故可以设置权重为5。其余缓存占用的内存比较小,可以设置权重为1。
注意:maximumWeight
和weigher
需要搭配使用。
2.2.2 基于时间的数据移除
API方法 | 作用 |
---|---|
expireAfterAccess | 根据某个键值对最后一次访问之后多少时间后移除 |
expireAfterWrite | 据某个键值对被创建或值被替换后多少时间移除 |
refreshAfterWrite&&LoadingCache | LoadingCache下缓存写入后异步刷新 |
详细请见——guavaCache本地缓存失效方案expireAfterWrites和refreshAfterWrites
private static Cache<String, String> cache = CacheBuilder.newBuilder().
expireAfterAccess(2, TimeUnit.SECONDS).
build();
2.2.3 基于引用的回收
引用类型 | 作用 |
---|---|
强引用 | 常见普通对象引用,只要还有强引用指向一个对象,GC不能回收 |
软引用 | JVM认为内存不足,才会试图回收软引用指向的对象 |
弱引用 | 一旦发生GC,就会回收弱引用指向的对象 |
虚引用 | 虚引用不会决定对象的生命周期 |
JVM那点事-对象的自救计划(对象被设为null会被回收吗?)
guava也可以设置value的引用类型,来实现基于引用的回收。
@Slf4j
public class CacheTest {
/**
* 缓存大小
*/
private static Cache<String, String> cache = CacheBuilder.newBuilder().
weakValues().
build();
private static void cache() throws InterruptedException {
cache.put("a", new String("a"));
//显式GC
System.gc();
System.out.println(cache.getIfPresent("a"));
}
}
执行后果
null
API方法 | 作用 |
---|---|
softValues | 软引用,JVM认为内存不足,才会试图回收软引用指向的对象 |
weakValues | 弱引用,一旦发生GC,就会回收弱引用指向的对象 |
2.2.4 removalListener移除缓存时的监听
@Slf4j
public class CacheTest {
/**
* 缓存大小
*/
private static Cache<String, String> cache = CacheBuilder.newBuilder().
maximumSize(2).
removalListener((notification) -> System.out.println("移除掉的缓存值" + //移除缓存的回调方法
notification.getCause().name() + ":" +
notification.getKey() + ":" +
notification.getValue())).
build();
private static void cache() throws InterruptedException {
cache.put("a", "a");
cache.put("b", "b");
cache.put("c", "c");
System.out.println(cache.getIfPresent("a"));
}
}
执行结果:
移除掉的缓存值SIZE:a:a
null
但是有点需要注意的是默认Removal Listener中的行为是和移除动作同步执行的,如果需要改成异步形式,可以考虑使用RemovalListeners.asynchronous(RemovalListener, Executor)
2.2.5 recordStats开启统计功能
@Slf4j
public class CacheTest {
private static Cache<String, String> cache = CacheBuilder.newBuilder().
maximumSize(2).
recordStats().
build();
private static void cache() throws InterruptedException {
cache.put("a", "a");
cache.put("b", "b");
cache.put("c", "c");
System.out.println(cache.getIfPresent("b"));
System.out.println(cache.getIfPresent("b"));
System.out.println(cache.getIfPresent("c"));
System.out.println(cache.getIfPresent("a"));
CacheStats stats = cache.stats();
System.out.println(JSON.toJSONString(stats));
}
}
效果图:
image.png
2.3 操作缓存的API
2.3.1 显式移除缓存
API方式 | 作用 |
---|---|
invalidate(key) | 单独移除 |
invalidateAll(keys) | 批量移除 |
invalidateAll() | 移除所有 |
2.3.2 维护缓存的API
API方式 | 作用 |
---|---|
get() | 存在返回缓存值,不存在则调用Callable<? extends V> loader 方法动态生成缓存值并存入缓存中 |
getIfPresent() | 若缓存存在则返回缓存,不存在则返回null |
put(K key, V value) | 填充缓存 |
asMap() | 将缓存转换为Map |
网友评论