Google Guava Cache
JAVA 8
guava 31.1-jre
---
序章
Guava is a suite of core and expanded libraries that include utility classes, Google's collections, I/O classes, and much more.
Guava Cache 的优点是封装了get,put操作;提供线程安全的缓存操作;提供过期策略;提供回收策略;缓存监控。当缓存的数据超过最大值时,使用LRU算法替换。
maven项目引入Guava:ben发布于博客园
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
注,最新 Mar 01, 2022,31.1-jre,一年多没更新了。
一个本地缓存。
最简单的使用
初始化一个 LoadingCache 对象(本地缓存),测试 读写数据。ben发布于博客园
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
/**
* Guava Cache 测试
*/
@Slf4j
public class GuavaCacheTest {
public static LoadingCache<String, Integer> cb;
static {
cb = CacheBuilder.newBuilder()
.build(new CacheLoader<String, Integer>() {
@Override
public Integer load(String key) throws Exception {
return null;
}
});
}
public static void main(String[] args) {
checkCb(cb);
test1();
}
private static void test1() {
String key = "key1";
Integer val = null;
try {
val = cb.get(key);
log.info("val 1={}", val);
} catch (Exception e) {
log.error("异常 1:e=", e);
}
cb.put(key, 1);
try {
val = cb.get(key);
log.info("val 2={}", val);
} catch (Exception e) {
log.error("异常 2:e=", e);
}
checkCb(cb);
}
private static void checkCb(LoadingCache<String, Integer> cb) {
log.info("cb={}, stats={}", cb, cb.stats());
}
}
测试结果:
cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0,
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
异常 1:e=
com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key key1.
at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2319)
at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2283)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2159)
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2049)
at com.google.common.cache.LocalCache.get(LocalCache.java:3966)
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3989)
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4950)
at com.lib.bootweb.test.GuavaCacheTest.test1(GuavaCacheTest.java:38)
at com.lib.bootweb.test.GuavaCacheTest.main(GuavaCacheTest.java:31)
val 2=1
cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0,
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
key 对应的值不存在时,返回异常。存在时,返回值。ben发布于博客园
由于 get(key) 会返回异常,可以使用 getIfPresent(key) 获取——不存在时返回null。
getIfPresent测试
private static void test2() {
IntStream.range(0, 5).forEach(cnt->{
String key = "nokey" + cnt;
Integer val = cb.getIfPresent(key);
System.out.println(key + ", val=" + val);
});
}
测试结果:
cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0,
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
nokey0, val=null
nokey1, val=null
nokey2, val=null
nokey3, val=null
nokey4, val=null
通过其返回值,可以判断 缓存中是否存在该值。ben发布于博客园
更多基本使用
asMap、invalidate
private static void test3() {
IntStream.range(0, 5).forEach(i->{
cb.put("key" + i, i * i);
});
// asMap()
log.info("1.cb.size={}", cb.size());
ConcurrentMap<String, Integer> cbMap = cb.asMap();
log.info("Map={}, class={}, values={}", cbMap.size(), cbMap.getClass(), cbMap);
// 使用 map 直接添加
cbMap.put("mapAdd", 123);
log.info("2.cb.size={}", cb.size());
log.info("Map={}, {}", cb.asMap(), cb.asMap().size());
// invalidate 删除元素
cb.invalidate("mapAdd");
cb.invalidate("key0");
cb.invalidate("key1");
log.info("3.cb.size={}", cb.size());
log.info("Map={}, {}", cb.asMap(), cb.asMap().size());
}
测试结果:
cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0, stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0,
loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
1.cb.size=5
Map=5, class=class com.google.common.cache.LocalCache, values={key4=16, key1=1, key2=4, key0=0, key3=9}
2.cb.size=6
Map={mapAdd=123, key4=16, key1=1, key2=4, key0=0, key3=9}, 6
3.cb.size=3
Map={key4=16, key2=4, key3=9}, 3
asMap() 返回了一个 ConcurrentMap<String, Integer> 对象,具体类型为 com.google.common.cache.LocalCache。
添加、获取、获取所有、删除,初步试验完毕。ben发布于博客园
至于 批量添加、批量删除、清空 等,大家可以自行摸索。
看源码
LoadingCache 接口
package com.google.common.cache;
@GwtCompatible
@ElementTypesAreNonnullByDefault
public interface LoadingCache<K, V> extends Cache<K, V>, Function<K, V> {
}
实现类:ben发布于博客园
Cache接口
package com.google.common.cache;
@DoNotMock("Use CacheBuilder.newBuilder().build()")
@GwtCompatible
@ElementTypesAreNonnullByDefault
public interface Cache<K, V> {
}
Function接口
package com.google.common.base;
@GwtCompatible
@FunctionalInterface
@ElementTypesAreNonnullByDefault
public interface Function<F extends @Nullable Object, T extends @Nullable Object>
extends java.util.function.Function<F, T> {
}
CacheBuilder 类
@GwtCompatible(emulated = true)
@ElementTypesAreNonnullByDefault
public final class CacheBuilder<K, V> {
}
进一步配置
上面的 LoadingCache<String, Integer> 基本上没有配置,这样的话,可以往这个 cb 缓存对象中 存取无限量的数据——最终把程序搞挂。
最佳实践是,根据项目需要、业务需要做适当的配置,避免误操作。ben发布于博客园
这些配置函数在 CacheBuilder 类 中,上面已展示,参考文档2中也有更多示例。
下面配置 缓存容量最大值 和 写入后10秒过期:
static {
cb = CacheBuilder.newBuilder()
// 最大容量 10
.maximumSize(10)
// 写入后 10秒 过期
.expireAfterWrite(Duration.ofSeconds(10))
.build(new CacheLoader<String, Integer>() {
@Override
public Integer load(String key) throws Exception {
return null;
}
});
}
测试程序:ben发布于博客园
private static void test4() {
log.info("1.put");
IntStream.range(0, 15).forEach(i->{
cb.put("key_" + i, i * i);
});
log.info("cb={}, {}", cb.size(), cb.asMap());
log.info("2.get");
IntStream.range(0, 15).forEach(i->{
log.info("i={}, val={}", i, cb.getIfPresent("key_" + i));
});
try {
TimeUnit.SECONDS.sleep(12);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("3.get");
IntStream.range(0, 15).forEach(i->{
log.info("i={}, val={}", i, cb.getIfPresent("key_" + i));
});
log.info("cb={}, {}", cb.size(), cb.asMap().size(), cb.asMap());
}
测试结果:ben发布于博客园
测试结果
21:33:43.186 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=com.google.common.cache.LocalCache$LocalLoadingCache@2c13da15,
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
21:33:43.206 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 1.put
21:33:43.258 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=10, {key_12=144, key_14=196, key_5=25, key_6=36, key_8=64, key_9=81,
key_11=121, key_7=49, key_13=169, key_10=100}
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 2.get
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=0, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=1, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=2, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=3, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=4, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=5, val=25
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=6, val=36
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=7, val=49
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=8, val=64
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=9, val=81
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=10, val=100
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=11, val=121
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=12, val=144
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=13, val=169
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=14, val=196
21:33:55.269 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 3.get
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=0, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=1, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=2, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=3, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=4, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=5, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=6, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=7, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=8, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=9, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=10, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=11, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=12, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=13, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=14, val=null
21:33:55.274 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=0, 0
“2.get”后面,只有 10条数据,和 “maximumSize(10)”有关;ben发布于博客园
休眠 12秒后,缓存中没有数据,和 “expireAfterWrite(Duration.ofSeconds(10))”有关。
注意区分 expireAfterWrite 和 expireAfterAccess。
---END---
ben发布于博客园
本文链接:
https://www.cnblogs.com/luo630/p/17212894.html
ben发布于博客园
参考资料
1、Guava LoadingCache详解及工具类
https://www.codenong.com/cs105556768/
2、Guava学习笔记:Guava cache
https://www.cnblogs.com/peida/p/Guava_Cache.html
3、Guava Cache 原理分析与最佳实践
by Java Punk
于 2022-07-27 16:41:39 发布
原文链接:https://blog.csdn.net/weixin_44259720/article/details/125890496
4、
ben发布于博客园