文章来源:https://www.cnblogs.com/CrankZ/p/10889859.html
说起Guava Cache,很多人都不会陌生,它是Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU算法实现,支持多种缓存过期策略。由于Guava的大量使用,Guava Cache也得到了大量的应用。但是,Guava Cache的性能一定是最好的吗?也许,曾经,它的性能是非常不错的。但所谓长江后浪推前浪,总会有更加优秀的技术出现。今天,我就来介绍一个比Guava Cache性能更高的缓存框架:Caffeine。
Tips: Spring5(SpringBoot2)开始用Caffeine取代guava.详见官方信息SPR-13797
https://jira.spring.io/browse/SPR-13797
1|1什么时候用
- 愿意消耗一些内存空间来提升速度
- 预料到某些键会被多次查询
- 缓存中存放的数据总量不会超出内存容量
1|2性能
由图可以看出,Caffeine不论读还是写的效率都远高于其他缓存。
这里只列出部分性能比较,详细请看官方官方 https://github.com/ben-manes/caffeine/wiki/Benchmarks
1|3依赖
我们需要在 pom.xml 中添加 caffeine 依赖:
版本问题参考https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine
1|4新建对象
参数含义
- initialCapacity: 初始的缓存空间大小
- maximumSize: 缓存的最大数量
- maximumWeight: 缓存的最大权重
- expireAfterAccess: 最后一次读或写操作后经过指定时间过期
- expireAfterWrite: 最后一次写操作后经过指定时间过期
- refreshAfterWrite: 创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存
- weakKeys: 打开key的弱引用
- weakValues:打开value的弱引用
- softValues:打开value的软引用
- recordStats:开发统计功能
注意:
expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
maximumSize和maximumWeight不可以同时使用
1|5异步
解释
1|6添加数据
Caffeine 为我们提供了三种填充策略:
手动、同步和异步
手动添加
很简单的
自动添加1(自定义添加函数)
// 这种写法可以简化成下面Lambda表达式
cache.get("hello", new Function<String, String>() {
@Override
public String apply(String k) {
return getValue(k);
}
});
// 可以简写为
cache.get("hello", k -> getValue(k));
自动添加2(初始添加)
和上面方法一样,只不过这个是在新建对象的时候添加
1|7过期策略
Caffeine提供三类驱逐策略:
- 基于大小(size-based)
- 基于时间(time-based)
- 基于引用(reference-based)
1、大小
输出结果
1、淘汰2个
2、淘汰并不是按照先后顺序,内部有自己的算法
2、时间
Caffeine提供了三种定时驱逐策略:
- expireAfterAccess(long, TimeUnit):在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。
- expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期。
- expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算。
缓存的删除策略使用的是惰性删除和定时删除。这两个删除策略的时间复杂度都是O(1)。
expireAfterWrite
结果
例子2
结果
expireAfterAccess
Access就是读和写
结果
读和写都没有的情况下,3秒后才过期
也可以同时用expireAfterAccess和expireAfterWrite方法指定过期时间,这时只要对象满足两者中的一个条件就会被自动过期删除。
expireAfter 和 refreshAfter 之间的区别
- expireAfter 条件触发后,新的值更新完成前,所有请求都会被阻塞,更新完成后其他请求才能访问这个值。这样能确保获取到的都是最新的值,但是有性能损失。
- refreshAfter 条件触发后,新的值更新完成前也可以访问,不会被阻塞,只是获取的是旧的数据。更新结束后,获取的才是新的数据。有可能获取到脏数据。
3、引用
- Caffeine.weakKeys() 使用弱引用存储key。如果没有其他地方对该key有强引用,那么该缓存就会被垃圾回收器回收。
- Caffeine.weakValues() 使用弱引用存储value。如果没有其他地方对该value有强引用,那么该缓存就会被垃圾回收器回收。
- Caffeine.softValues() 使用软引用存储value。
结果
解释:当给value2引用赋值一个新的对象之后,就不再有任何一个强引用指向原对象。System.gc()触发垃圾回收后,原对象就被清除了。
简单回顾下Java中的四种引用
Java4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 在内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 在垃圾回收时 | 对象缓存 | GC运行后终止 |
虚引用 | Unknown | Unknown | Unknown |
1|8显式删除缓存
除了通过上面的缓存淘汰策略删除缓存,我们还可以手动的删除
1|9淘汰、移除监听器
可以为缓存对象添加一个移除监听器,这样当有记录被删除时可以感知到这个事件。
结果
1|10统计
结果
除了结果输出的内容,CacheStats还可以获取如下数据。
2|0参考
http://oopsguy.com/2017/10/25/java-caching-caffeine/
https://juejin.im/post/5b8df63c6fb9a019e04ebaf4
https://www.jianshu.com/p/9a80c662dac4
https://www.sohu.com/a/235729991_100109711
https://www.cnblogs.com/yueshutong/p/9381540.html
https://blog.csdn.net/qq_38974634/article/details/80650810
https://blog.csdn.net/qq_32867467/article/details/82944506
https://blog.csdn.net/grafx/article/details/80462628
http://ifeve.com/google-guava-cachesexplained/