Java 第三方包-Guava Cache

wiki

Guava 中的缓存是本地缓存的实现,与 ConcurrentMap 相似,但提供了自动回收方法,以此限制内存的使用.

1. 创建

1.1. Cache

Cache 是最基本缓存接口

1
2
3
4
5
6
7
8
public void cacheCreateTest() {
Cache<String,String> cache = CacheBuilder.newBuilder()
.maximumSize(100) //设置缓存最大容量
.expireAfterWrite(1,TimeUnit.MINUTES) //过期策略,写入一分钟后过期
.build();
cache.put("a","a1");
String value = cache.getIfPresent("a");
}

1.2. LoadingCache

LoadingCache 继承自 Cache ,当从缓存中读取某个 key 时,假如没有读取到值,其会自动进行加载数据到缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void loadingCacheTest() throws ExecutionException {
LoadingCache<String,String> loadingCache = CacheBuilder.newBuilder()
.maximumSize(3)
.refreshAfterWrite(Duration.ofMillis(10)) //10分钟后刷新缓存的数据
.build(new CacheLoader<String, String>() {
@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"));
}

2. 常用参数

2.1. 容量初始化

1
2
3
4
5
public void initialCapacityTest(){
Cache<String,String> cache = CacheBuilder.newBuilder()
.initialCapacity(1024) //初始容量
.build();
}

2.2. 最大容量

最大容量可以通过两种维度来设置:

  • maximumSize : 最大记录数,存储数据的个数
  • maximumWeight : 最大容量,存储数据的大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void maxSizeTest(){
Cache<String,String> cache = 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<String,String> cache1 = CacheBuilder.newBuilder()
.maximumWeight(1024 * 1024 * 1024) //最大容量为1M
.weigher(new Weigher<String, String>() {
//用来计算容量的 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"));

}

2.3. 过期时间

  • expireAfterWrite : 写入后多长时间,数据就过期了
  • expireAfterAccess : 数据多长时间没有被访问,就过期
1
2
3
4
5
6
7
8
9
10
11
12
13
 public void expireTest() throws InterruptedException {
Cache<String,String> cache = 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);
}
}

2.4. 引用

1
2
3
4
5
Cache<String,String> cache = CacheBuilder.newBuilder()
.weakKeys() //使用弱引用存储键。当键没有其它(强或软)引用时,该缓存可能会被回收。
.weakValues() //使用弱引用存储值。当值没有其它(强或软)引用时,该缓存可能会被回收。
.softValues() //使用软引用存储值。当内存不足并且该值其它强引用引用时,该缓存就会被回收
.build();

3. 回收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void invalidateTest(){
Cache<String,String> cache = 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<String> 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"));
}

4. 数据清除监听器

可以给 Cache 中的对象加一个监听,当有对象被删除时会有事件通知。

创建 Cache 时 removalListener 方法传入一个 RemovalListener 对象,重写 onRemoval 方法来进行数据清除事件的监听。

三种情况下的清除数据都会被监听:

  • 超过容量,清除最早添加进缓存的数据
  • 超过设定的过期时间,缓存系统自动删除
  • 手动清除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void removeListenerTest() throws InterruptedException {
Cache<String,String> cache = CacheBuilder.newBuilder()
.maximumSize(3)
.expireAfterWrite(Duration.ofSeconds(5)) //5秒后自动过期
.removalListener(new RemovalListener<Object, Object>() {
//添加一个remove的监听器
@Override
public void onRemoval(RemovalNotification<Object, Object> 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");

}

5. 统计信息

我们在使用缓存的时候,应该要关心缓存的命中率、加载数据时间等等信息,可以根据这些统计信息来对缓存进行优化调整,让缓存更好的为我们服务。在构建缓存对象时,可以开启统计信息,开启后会对缓存的操作进行统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void recordStatsTest(){
Cache<String,String> cache = 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());
}