springboot中使用LoadingCache缓存需要在pom中引用依赖
依赖
引用的依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
方法介绍
CacheBuilder:是 LoadingCache实现类,存储键值形式(本质Map)。用来定义各种规则。
最常用的方法的介绍:
- 定义对象个数的限制,默认的过期规则是(没有定义过期规则)是在超出限制的时候,会将最远时间最少使用的自动过期保证总数在设置的范围内
public CacheBuilder<K, V> maximumSize(long size)
- 定义过期时间显示(duration:设置持续时间;unit:持续时间单位)缓存中数值在指定时间没有值更新会过期。
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit)
- 同样是定义过期时间,只要缓存中的数据被访问就会重置数据的缓存时间。
public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit)
refreshAfterWrite(long duration, TimeUnit unit)
- 同样是定义过期时间,只是重新加载过期数据的方法不一样,属于多线程操作。
public CacheBuilder<K, V> refreshAfterWrite(long duration, TimeUnit unit)
使用方法:
使用 expireAfterWrite(long duration, TimeUnit unit) 和 expireAfterAccess(long duration, TimeUnit unit)方法
需要继承和实现CacheLoader 的方法== load(String name)== ,该方法用于过期数据重新加载,或者是初始化数据
V load(K key);
- 可以不实现逻辑,即返回null。
- 可以做一些数据初始化,但是这个操作可能会被多次执行。原因:当我们调用 LoadingCache 的 get(String key) 时,首先会从缓存里获取数据,如果没有就会调用该方法。将该方法的返回数据。和传入的name组装成 key-value 加入缓存中。所以当我们多次调用get(String key).但是缓存中有没有时 load(String name) 就会被多次执行。但是同一个name只会被执行一次(默认不是返回null的情况下)。
- 使用了expireAfterWrites之后,每次缓存失效LoadingCache都会去调用我们实现的 load() 方法去重新加载缓存,在加载期间,所有线程对该缓存key的访问都将被block。所以如果实际加载缓存需要较长时间的话,这种方式不太适用。还需要注意的是,这个主动调用load() 方法去重新加载缓存 只是在被调用 get(String key)方法的时候。
测试代码如下:
public class LoadingCacheTest {
/**
* 第一种直接定义,但是要有一个实现类 该实现类 继承 CacheLoader 并实现 load(String key) 方法
*/
private static LoadingCache<String, Map<String, Object>> listLoadingCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build(new MyCacheLoader());
public static void main(String[] args) {
//SpringApplication.run(UsulTestStartApplication.class, args);
Runnable runnable1 = () -> {
try {
Map<String, Object> map = new HashMap<>();
map.put("333333","3143134");
listLoadingCache.put("333333",map);
System.out.println("key:333333------value:" + map.get("333333"));
map = listLoadingCache.get("1111111");
System.out.println("key:fafda------value:" + map.get("fafda"));
map = listLoadingCache.get("1111111");
System.out.println("key:fafda------value:" + map.get("fafda"));
map = listLoadingCache.get("22222222");
System.out.println("key:fafda------value:" + map.get("fafda"));
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
Thread.currentThread().sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(Map.Entry<String, Map<String, Object>> entry : listLoadingCache.asMap().entrySet()){
System.out.println(entry.getKey() + "---第一次---" + "key:fafda------value:" + entry.getValue().get("fafda"));
}
Map<String, Object> map = null;
try {
map = listLoadingCache.get("22222222");
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("key:fafda------value:" + map.get("fafda"));
};
Thread thread1 = new Thread(runnable1);
thread1.start();
}
private static class MyCacheLoader extends CacheLoader<String,Map<String, Object>> {
@Override
public Map<String, Object> load(String name) throws Exception {
System.out.println("数据初始化:" + name);
Map<String, Object> map = new HashMap<>();
map.put("fafda","dfadada");
return map;
}
}
}
expireAfterAccess(long duration, TimeUnit unit) 方法就不在介绍了,只要将睡眠时间条短一点就能册数出来。
使用 refreshAfterWrite(long duration, TimeUnit unit)方法
使用方法时需要同时实现 CacheLoader 的 两个方法:
public V load(K key); //该方法用于初始化数据,或者第一次获取数据时缓存中没有就会调用该方法。
public ListenableFuture<V> reload(K key, V oldValue); //过期数据从新加载方式,异步实现
在使用refreshAfterWrites之后,需要自己实现CacheLoader的reload方法,在方法中创建一个ListenableFutureTask,然后将这个task提交给线程池去异步执行,reload方法最后返回这个ListenableFutureTask。这样做使缓存失效后重新加载就变成了异步,加载期间尝试获取缓存的线程也都不会被block,而是获取到加载之前的值。当加载完毕之后,各个线程就能取到最新值了。
测试方法:
public class LoadingCacheTest {
private static ExecutorService executorService = Executors.newFixedThreadPool(8);
private static LoadingCache<String, Map<String, Object>> loadingCache = CacheBuilder.newBuilder().refreshAfterWrite(5, TimeUnit.SECONDS).build(new MyCacheLoader());
public static void main(String[] args) {
Runnable runnable2 = () -> {
try {
Map<String, Object> map = new HashMap<>();
map.put("333333","3143134");
loadingCache.put("333333",map);
System.out.println("key:333333------value:" + map.get("333333"));
map = loadingCache.get("1111111");
System.out.println("key:fafda------value:" + map.get("fafda"));
map = loadingCache.get("1111111");
System.out.println("key:fafda------value:" + map.get("fafda"));
map = loadingCache.get("22222222");
System.out.println("key:fafda------value:" + map.get("fafda"));
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
Thread.currentThread().sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map<String, Object> map = null;
try {
map = loadingCache.get("22222222");
System.out.println("失效重新加载中:key:fafda------value:" + map.get("fafda"));
Thread.currentThread().sleep(20000);
map = loadingCache.get("22222222");
System.out.println("失效后已经加载完成:key:fafda------value:" + map.get("fafda"));
} catch (Exception e) {
e.printStackTrace();
}
};
Thread thread1 = new Thread(runnable2);
thread1.start();
}
private static class MyCacheLoader extends CacheLoader<String,Map<String, Object>> {
@Override
public Map<String, Object> load(String name) throws Exception {
System.out.println("load数据初始化:" + name);
Map<String, Object> map = new HashMap<>();
map.put("fafda","dfadada");
return map;
}
@Override
public ListenableFuture<Map<String, Object>> reload(String key, Map<String, Object> oldValue) throws Exception{
System.out.println("reload数据初始化:" + key + "----" +oldValue);
ListenableFutureTask<Map<String, Object>> futureTask = ListenableFutureTask.create(() -> {
Map<String, Object> map = new HashMap<>();
Thread.currentThread().sleep(10000);
map.put("fafda","111111111");
return map;
});
executorService.execute(futureTask);
return futureTask;
}
}
}
测试结果:
key:333333------value:3143134
load数据初始化:1111111
key:fafda------value:dfadada
key:fafda------value:dfadada
load数据初始化:22222222
key:fafda------value:dfadada
reload数据初始化:22222222----{fafda=dfadada}
失效重新加载中:key:fafda------value:dfadada
reload数据初始化:22222222----{fafda=111111111}
失效后已经加载完成:key:fafda------value:111111111
由测试结果可以知道。当数据失效时,使用refreshAfterWrite()方法获取到的是旧的值。缓存的刷新是异步的。刷新之后就能获取到最新的值。
以上就是 LoadingCache缓存的使用介绍。有任何错误之处欢迎指正,谢谢。