在java中,缓存是在计算机上的一个原始数据的复制集。通俗的说就是数据的一个备份。
就是将程序或系统经常要调用的对象或者数据存在内存中,再次调用时可以快速从内存中获取对象,不必再去创建新的
重复的实例或者不用从处理数据速度比较慢的磁盘上获取数据。
由于内存的容量比较小,但是磁盘的容量比较大,所以一般都是把热数据(访问比较频繁的数据存放到缓存中去,这样
系统程序就能快速的访问到热点数据,然后快速做出反应,提高了程序流畅性的同时,也提高了用户的体验)
缓存的填充方式有三种,手动、同步和异步,下面分别简单介绍下每种方式使用的API:
/**
* 手动填充测试
*/
public void cacheTest(String k) {
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.build();
cache.put("c1", "c1");
//获取缓存值,如果为空,返回null
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("cacheTest default: [{}] -> [{}]", k, cache.get(k, this::buildLoader));
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
//清除缓存
cache.invalidate(k);
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
}
private String buildLoader(String k) {
return k + "+default";
}
/**
* 同步填充测试
*/
public void loadingCacheTest(String k) {
//同步填充在build方法指定表达式
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.build(this::buildLoader);
loadingCache.put("c1", "c1");
log.info("loadingCacheTest get: [{}] -> [{}]", k, loadingCache.get(k));
//获取缓存值,如果为空,返回null
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("loadingCacheTest default: [{}] -> [{}]", k, loadingCache.get(k, this::buildLoader));
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
loadingCache.invalidate(k);
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
}
private String buildLoader(String k) {
return k + "+default";
}
/**
* 异步填充测试
*/
public void asyncLoadingCacheTest(String k) throws ExecutionException, InterruptedException {
//异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。
//
//如果要以同步方式调用时,应提供CacheLoader。要以异步表示时,应该提供一个AsyncCacheLoader,并返回一个CompletableFuture。
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.buildAsync(s -> this.buildLoaderAsync("123").get());
log.info("asyncLoadingCacheTest get: [{}] -> [{}]", k, asyncLoadingCache.get(k).get());
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("asyncLoadingCacheTest default: [{}] -> [{}]", k, asyncLoadingCache.get(k, this::buildLoader).get());
}
private CompletableFuture<String> buildLoaderAsync(String k) {
return CompletableFuture.supplyAsync(() -> k + "+buildLoaderAsync");
}
/**
* 手动异步测试
*/
public void asyncManualCacheTest(String k) throws ExecutionException, InterruptedException {
//异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。
//
//如果要以同步方式调用时,应提供CacheLoader。要以异步表示时,应该提供一个AsyncCacheLoader,并返回一个CompletableFuture。
AsyncCache<String, String> asyncCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.buildAsync();
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("asyncManualCacheTest default: [{}] -> [{}]", k, asyncCache.get(k, this::buildLoader).get());
}
Caffeine的缓存清除是惰性的,可能发生在读请求后或者写请求后,比如说有一条数据过期后,不会立即删除,可能在下一次读/写操作后触发删除(类比于redis的惰性删除)。如果读请求和写请求比较少,但想要尽快的删掉cache中过期的数据的话,可以通过增加定时器的方法,定时执行cache.cleanUp()方法(异步方法,可以等待执行),触发缓存清除操作。
/**
* 淘汰策略-size
*/
public void sizeTest() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(1)
.build(this::buildLoader);
List<String> list = Lists.newArrayList("c1", "c2", "c3");
loadingCache.put(list.get(0), list.get(0));
log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
loadingCache.put(list.get(1), list.get(1));
log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
loadingCache.put(list.get(2), list.get(2));
log.info("weightTest get: [{}] -> [{}]", list.get(2), loadingCache.get(list.get(2)));
log.info("weightTest cache map:{}", loadingCache.getAll(list));
}
/**
* 淘汰策略-weight
*/
public void weightTest() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumWeight(10)
.weigher((key, value) -> 5)
.build(this::buildLoader);
List<String> list = Lists.newArrayList("c1", "c2", "c3");
loadingCache.put(list.get(0), list.get(0));
log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
loadingCache.put(list.get(1), list.get(1));
log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
log.info("weightTest cache map:{}", loadingCache.getAll(list));
}
基于时间过期
(1) expireAfterAccess():缓存访问后,一定时间失效;即最后一次访问或者写入开始时计时。
(2) expireAfterWrite():缓存写入后,一定时间失效;以写入缓存操作为准计时。
(3) expireAfter():自定义缓存策略,满足多样化的过期时间要求。
/**
* 淘汰策略-time
*/
public void timeTest() {
//1.缓存访问后,一定时间后失效
LoadingCache<String, String> loadingCacheOne = Caffeine.newBuilder()
.expireAfterAccess(10L, TimeUnit.SECONDS)
.build(this::buildLoader);
//2.缓存写入后,一定时间后失效
LoadingCache<String, String> loadingCacheTwo = Caffeine.newBuilder()
.expireAfterWrite(10L, TimeUnit.SECONDS)
.build(this::buildLoader);
//3.自定义过期策略
LoadingCache<String, String> loadingCacheThree = Caffeine.newBuilder()
.expireAfter(new Expiry<Object, Object>() {
@Override
public long expireAfterCreate(@NonNull Object o, @NonNull Object o2, long l) {
return 0;
}
@Override
public long expireAfterUpdate(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
return 0;
}
@Override
public long expireAfterRead(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
return 0;
}
})
.build(this::buildLoader);
}
Caffeine中提供了手动进行缓存的删除,无需等待我们上面提到的被动的一些删除策略,使用方法如下:
cacheOne.invalidateAll();
cacheOne.invalidate(Object o);
cacheOne.invalidateAll(List);
移除事件RemovalListener是一种缓存监听事件,当key被移除的时候就会触发这个方法,可以进行一些相关联的操作。RemovalListener可以获取到key、value和RemovalCause(删除的原因)。另外RemovalListener中操作是线程池异步执行的。
/**
* 移除监听器
*/
public void removeTest() throws InterruptedException {
Cache<String, String> cacheOne = Caffeine.newBuilder()
.maximumSize(1)
.removalListener(
(o, o2, removalCause) ->
System.out.println(o + " is " + "remove" + " reason is " + removalCause.name()))
.build();
}
说明:本文有些许参考之处,而且有个人观点杂糅其中,如果有大神发现不当之处,还请不吝赐告,笔者定当虚心受教,谢谢!
站在巨人的肩膀上:https://blog.csdn.net/l_dongyang/article/details/108326755