一、Guava Cache
一般在项目中,本地缓存的实现为 ConcurrentHashMap
,它具有线程安全、持久有效的特点。但是相较于传统缓存,它不具备缓存过期、缓存移除等特性,Google Guava
包内的 Cache
模块可能会给你一个新的选择。
Guava 目前托管于 GitHub,在项目中引入也是十分简单:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
<!-- or, for Android: -->
<version>27.1-android</version>
</dependency>
二、初始化
2.1 CacheLoader
如果我们希望对缓存中所有的 key 值使用同一个加载策略,那么推荐使用 CacheLoader
方式加载。假设我们需要缓存的数据为:
private static Map<String, List<Integer>> data = new HashMap<String, List<Integer>>() {
{
put("nanjing", new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9)));
put("beijing", new ArrayList<>(Arrays.asList(2, 4, 6, 8, 10)));
put("shanghai", new ArrayList<>(Arrays.asList(0, -1, 11)));
}};
使用 CacheLoader
方式初始化:
LoadingCache<String, List<Integer>> cache = CacheBuilder.newBuilder()
.maximumSize(3)
.build(new CacheLoader<String, List<Integer>>() {
@Override
public List<Integer> load(String i) throws Exception {
return data.get(i);
}
});
其中 maximumSize()
为缓存的最大容量,当缓存超过最大容量时,基于 LRU
清理缓存。load()
方法为缓存未命中时的加载策略。
同时使用 get(K)
方法进行缓存读取:
try {
System.out.println(cache.get("nanjing"));
} catch (ExecutionException e) {
e.printStackTrace();
}
// output: [1, 3, 5, 7, 9]
调用时优先读取缓存,如果缓存中不存在该 key 值,则调用初始化时的
load()
方法,将该 key 值存入缓存,再返回。
2.2 Callable
Callable
则是另一种较为灵活的初始化形式,它在初始化的时候不指明加载策略:
Cache<String, List<Integer>> cache2 = CacheBuilder.newBuilder().maximumSize(3).build();
在读取时再指定缓存的加载策略:
try {
List<Integer> list = cache2.get("nanjing", new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return data.get("nanjing");
}
});
System.out.println(list);
} catch (ExecutionException e) {
e.printStackTrace();
}
// output: [1, 3, 5, 7, 9]
一般情况下,使用
CacheLoader
初始化,如果你需要更为灵活的缓存加载策略,使用Callable
初始化。
三、初始化属性
在上一节初始化的时候使用了 maximumSize
属性,除此之外,还有这些常用属性:
属性 | 描述 |
---|---|
concurrencyLevel(4) | 并发级别为8,即可以同时写缓存的线程数,默认为4。 |
initialCapacity(10) | 缓存的初始容量,默认为10 |
removalListener() | 指定缓存的移除通知 |
expireAfterAccess(8, TimeUnit.SECONDS) | 缓存项在给定时间内没有被读/写访问,则回收。 |
expireAfterWrite(8, TimeUnit.SECONDS) | 缓存项在给定时间内没有被写访问(新增或覆盖),则回收。 |
refreshAfterWrite(8, TimeUnit.SECONDS) | 缓存项在写访问(新增或覆盖)后给定时间,刷新。 |
四、基本操作
4.1 显式插入
Guava Cache 也支持使用 put(key, value)
方法手动的向缓存中插入数据,这会直接覆盖掉缓存中已存在的值。例如:
cache.put("guangzhou", new ArrayList<>(Arrays.asList(101, 110, 222)));
4.2 显式清除
你可以手动的清除缓存项,主要方法包括:
- 根据 key 清除:
cache.invalidate(key)
- 批量清除:
cache.invali