本文共 8273 字,大约阅读时间需要 27 分钟。
Guava Cache的使用场景:
示例代码
@Test public void cacheCreateTest(){ Cachecache = CacheBuilder.newBuilder() .maximumSize(100) //设置缓存最大容量 .expireAfterWrite(1,TimeUnit.MINUTES) //过期策略,写入一分钟后过期 .build(); cache.put("a","a1"); String value = cache.getIfPresent("a"); }
Cache
Cache是Guava提供的最基本缓存接口,创建一个Cache很简单
Cache是通过CacheBuilder对象来build出来的,build之前可以设置一系列的参数
LoadingCache
LoadingCache继承自Cache,当从缓存中读取某个key时,假如没有读取到值,LoadingCache会自动进行加载数据到缓存
@Test public void loadingCacheTest() throws ExecutionException { LoadingCacheloadingCache = CacheBuilder.newBuilder() .maximumSize(3) .refreshAfterWrite(Duration.ofMillis(10))//10分钟后刷新缓存的数据 .build(new CacheLoader () { @Override public String load(String key) throws Exception { Thread.sleep(1000); System.out.println(key + " load data"); return key + " add value"; } }); System.out.println(loadingCache.get("a")); System.out.println(loadingCache.get("b")); }
运行结果:
a load dataa add valueb load datab add value
LoadingCache也是通过CacheBuilder创建出来的,只不过创建的时候,需要在build方法里面传入CacheLoader
CacheLoader类的load方法就是在key找不到的情况下,进行数据自动加载的
下面我们看一下Guava Cache在使用时常用的属性,对Cache和LoadingCache都适用
1、容量初始化
public void initialCapacityTest(){ Cachecache = CacheBuilder.newBuilder() .initialCapacity(1024) //初始容量 .build(); }
2、最大容量
最大容量可以通过两种维度来设置
@Test public void maxSizeTest(){ Cachecache = CacheBuilder.newBuilder() .maximumSize(2)//缓存最大个数 .build(); cache.put("a","1"); cache.put("b","2"); cache.put("c","3"); System.out.println(cache.getIfPresent("a")); System.out.println(cache.getIfPresent("b")); System.out.println(cache.getIfPresent("c")); Cache cache1 = CacheBuilder.newBuilder() .maximumWeight(1024 * 1024 * 1024)//最大容量为1M //用来计算容量的Weigher .weigher(new Weigher () { @Override public int weigh(String key, String value) { return key.getBytes().length + value.getBytes().length; } }) .build(); cache1.put("x","1"); cache1.put("y","2"); cache1.put("z","3"); System.out.println(cache1.getIfPresent("x")); System.out.println(cache1.getIfPresent("y")); System.out.println(cache1.getIfPresent("z")); }
运行结果:
null23123
我们设置缓存的最大记录为2,当我们添加三个元素进去后,会把前面添加的元素覆盖
3、过期时间
@Test public void expireTest() throws InterruptedException { Cachecache = CacheBuilder.newBuilder() .maximumSize(100)//缓存最大个数 .expireAfterWrite(5,TimeUnit.SECONDS)//写入后5秒过期 .build(); cache.put("a","1"); int i = 1; while(true){ System.out.println("第" + i + "秒获取到的数据为:" + cache.getIfPresent("a")); i++; Thread.sleep(1000); } }
运行结果
第1秒获取到的数据为:1第2秒获取到的数据为:1第3秒获取到的数据为:1第4秒获取到的数据为:1第5秒获取到的数据为:1第6秒获取到的数据为:null第7秒获取到的数据为:null第8秒获取到的数据为:null第9秒获取到的数据为:null
@Test public void expireAfterAccessTest() throws InterruptedException { Cachecache = CacheBuilder.newBuilder() .maximumSize(100)//缓存最大个数 .expireAfterAccess(5,TimeUnit.SECONDS)//5秒没有被访问,就过期 .build(); cache.put("a","1"); Thread.sleep(3000); System.out.println("休眠3秒后访问:" + cache.getIfPresent("a")); Thread.sleep(4000); System.out.println("休眠4秒后访问:" + cache.getIfPresent("a")); Thread.sleep(5000); System.out.println("休眠5秒后访问:" + cache.getIfPresent("a")); }
运行结果:
休眠3秒后访问:1休眠4秒后访问:1休眠5秒后访问:null
从运行结果可以看出,只要超过了设定的时间没有人访问,缓存的数据就会过期
回收策略
在上面我们讲了两种回收策略
Guava Cache还支持基于引用级别的回收,这种回收策略是Java特有的,在Java的对象回收机制中,按对象的强弱可以分为强引用、软引用、弱引用、虚引用
强引用
强引用是我们最常用的引用,比如我们直接new一个对象,就是一个强引用
Mapmap = new HashMap ();
强引用不会被自动回收,当内存不足时直接报内存溢出
软引用
软引用是一种不稳定的引用方式,如果一个对象具有软引用,当内存充足时,GC不会主动回收软引用对象,而当内存不足时软引用对象就会被回收
SoftReference
弱引用
弱引用是一种比软引用更不稳定的引用方式,因为无论内存是否充足,弱引用对象都有可能被回收
WeakReferenceweakRef = new WeakReference (new Object()); Object obj = weakRef.get(); // 获取弱引用
虚引用
虚引用这种引用方式就是形同虚设,因为如果一个对象仅持有虚引用,那么它就和没有任何引用一样。在实践中也几乎没有使用
在Guava中支持软/弱引用的回收方式
Cachecache = CacheBuilder.newBuilder() .weakKeys() //使用弱引用存储键。当键没有其它(强或软)引用时,该缓存可能会被回收。 .weakValues() //使用弱引用存储值。当值没有其它(强或软)引用时,该缓存可能会被回收。 .softValues() //使用软引用存储值。当内存不足并且该值其它强引用引用时,该缓存就会被回收 .build();
手动回收
上面讲的都是缓存自动回收的策略,我们也可以调用Guava Cache提供的方法来手动清除
可以删除单个key,也可以批量删除key,同时也可以清除整个缓存的数据(谨慎使用哦~)
@Test public void invalidateTest(){ Cachecache = CacheBuilder.newBuilder().build(); cache.put("a","1"); cache.put("b","2"); //从缓存中清除key为a的数据 cache.invalidate("a"); System.out.println(cache.getIfPresent("a")); cache.put("x","x1"); cache.put("y","y1"); System.out.println("x清除之前:"+ cache.getIfPresent("x")); System.out.println("y清除之前:"+ cache.getIfPresent("y")); List list = Lists.newArrayList("x","y"); //批量清除 cache.invalidateAll(list); System.out.println("x清除之后:"+ cache.getIfPresent("x")); System.out.println("y清除之后:"+ cache.getIfPresent("y")); cache.put("y","y1"); cache.put("z","z1"); //清空缓存所有的数据 cache.invalidateAll(); System.out.println("全部清除后:" + cache.getIfPresent("y")); System.out.println("全部清除后:" + cache.getIfPresent("z")); }
运行结果:
nullx清除之前:x1y清除之前:y1x清除之后:nully清除之后:null全部清除后:null全部清除后:null
数据清除监听器
可以给Cache中的对象加一个监听,当有对象被删除时会有事件通知
@Test public void removeListenerTest() throws InterruptedException { Cachecache = CacheBuilder.newBuilder() .maximumSize(3) .expireAfterWrite(Duration.ofSeconds(5))//5秒后自动过期 //添加一个remove的监听器 .removalListener(new RemovalListener () { @Override public void onRemoval(RemovalNotification notification) { System.out.println("[" +notification.getKey() + ":" + notification.getValue() + "] 被删除了"); } }) .build(); cache.put("a","1"); Thread.sleep(2000); cache.put("b","2"); cache.put("c","3"); Thread.sleep(2000); cache.put("d","4"); Thread.sleep(5000); cache.put("e","5"); cache.invalidate("e"); }
运行结果:
[a:1] 被删除了[b:2] 被删除了[c:3] 被删除了[d:4] 被删除了
创建Cache时removalListener方法传入一个RemovalListener对象,重写onRemoval方法来进行数据清除事件的监听
从运行结果可以看出来,三种情况下的清除数据都会被监听
统计信息
我们在使用缓存的时候,应该要关心缓存的命中率、加载数据时间等等信息,可以根据这些统计信息来对缓存进行优化调整,让缓存更好的为我们服务。在构建缓存对象时,可以开启统计信息,开启后会对缓存的操作进行统计
@Test public void recordStatsTest(){ Cachecache = CacheBuilder.newBuilder() .maximumSize(3) .recordStats() .build(); cache.put("a","1"); cache.put("b","2"); cache.put("c","3"); cache.put("d","4"); cache.put("e","5"); cache.put("f","6"); cache.getIfPresent("a"); cache.getIfPresent("a"); cache.getIfPresent("e"); cache.getIfPresent("f"); cache.getIfPresent("h"); cache.getIfPresent("t"); System.out.println(cache.stats()); }
运行结果:
CacheStats{hitCount=2, missCount=4, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=3}
参考:
https://www.cnblogs.com/fnlingnzb-learner/p/11022152.html
https://blog.csdn.net/l1028386804/article/details/102764951