引申
1.Guava Cache和Ehcache一样也是本地缓存,虽然都是本地缓存,但是在细分领域中也还是有不同的应用场景,Guava是Google提供的一套Java工具包,而GuavaCache作为Guava的Cache部分而提供了一套非常完善的本地缓存机制。在Guava之前,JDK的ConcurrentHashMap因为能友好的支持并发而被经常用作本地缓存,但它毕竟还是个Map,不具备缓存的一些特性,比如缓存过期,缓存数据的加载/刷新等。
guava cache 适用场景
1.愿意消耗一些本地内存空间来提升速度。
一些数据对一致性要求不高,就可以不用放到Redis等集中缓存中,这样频繁读取还会增加网络开销,同时也需要考虑集中缓存宕机的情况。我们在使用本地内存做缓存的时候,也需要考虑缓存的数据总量不能超出服务器内存,这样就应该做一些数据淘汰机制来确保
2.更新锁定
当请求查询某一个key的时候,如果不存在则从源中读取,然后再回填到本地缓存中,这时如果并发量非常大,可能会有多个请求同时从源中读取数据,然后再回填到本地缓存,造成多次执行的情况。Guava Cache可以在CacheLoader的load方法中加以控制,对同一个key,只让一个请求去源中读取数据,而其他请求阻塞等待结果。
guava cache 的实现 CacheLoader
- Guava Cache是一个全内存的本地缓存实现,它提供了线程安全的实现机制。CacheLoader,Callable callback
- CacheLoader创建方式在构建Cache对象的时候定义一个CacheLoader来获取数据,在缓存不存在的时候能够自动加载数据到缓存中。这种创建方式适用的场景是:在创建的时候采用指定的加载缓存的方式,经常用作从数据库中获取和缓存数据。
3.Guava的cache数据删除的方式有两种,分别是主动删除和被动删除。 - 被动删除
基于数据大小的删除;按照缓存的大小来删除,如果缓存容量即将到达指定的大小时,就会把不常用的键值对从cache中移除。使用CacheBuilder.maximumSize(size)方法进行设置。说明:? size指的是记录数,不是容量。? 并不是超过缓存容量才会删除数据,而是接近的时候开始。
基于过期时间删除;在Guava Cache中提供了两个方法可以基于过期时间删除? expireAfterAccess(long, TimeUnit):某个key最后一次访问后,再隔多长时间后删除。? expireAfterWrite(long, TimeUnit):某个key被创建后,再隔多长时间后删除
基于引用的删除;这种方式主要是基于Java的垃圾回收机制,判断缓存的数据引用的关系,如果没有被引用,则Guava Cache会将该数据删除 - 主动删除
/**
* @author ngLee
* @version 1.0
* @Desc guava cache 本地缓存的实现 -- CacheLoader
* @date 2021/3/28 21:56
*/
public class TLocalCache {
public static LoadingCache<String,String> cache = CacheBuilder.newBuilder()
//设置并发级别,同时可以缓存的线程数
.concurrencyLevel(10)
//被动删除:某个key被创建后,再隔8秒后删除
.expireAfterWrite(8, TimeUnit.SECONDS)
//被动删除:个key最后一次访问后,再隔多2秒后删除。
.expireAfterAccess(2,TimeUnit.SECONDS)
//设置缓存的初始容量
.initialCapacity(10)
//被动删除:按照缓存的大小来删除如果缓存容量即将到达指定的大小时,就会把不常用的键值对从cache中移除
.maximumSize(14)
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
System.out.println("过期移除:key="+removalNotification.getKey()+"--value="+
removalNotification.getValue()+"--移除原因="+removalNotification.getCause());
}
}).build(new CacheLoader<String, String>() {
//build方法可以指定CacheLoader,在缓存不存在通过CacheLoader的实现自动加载
@Override
public String load(String key) throws Exception {
System.out.println("缓存不存在,自动加载:"+ key);
String vu = key +"对应的值";
return vu;
}
});
public static void main(String[] args) {
List<String> stringList = Lists.newArrayList("a","b","c","d","e","f","g","h"
,"i","l","m","n","o","p","x","y","z","q","w","r","ad","ac","bn","dd","ds","a","b","c","d","e");
stringList.forEach(x->{
try {
String strV = cache.get(x);
System.out.println("值:"+strV);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println("cache status:");
System.out.println(cache.stats().toString());
}
}
guava cache 的实现 Callable
Callable方式这个方法返回缓存中相应的值,如果未获取到缓存值则调用Callable方法。这个方法简便地实现了“如果有缓存则返回,否则读取、缓存、然后返回”的模式。适用的场景是:这种方式比较灵活,可以在获取缓存的指定Callable对象,在缓存中获取不到数据的时候,可以动态决定采用哪种方式加载数据到缓存
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import java.util.concurrent.Callable;
/**
* @author ngLee
* @version 1.0
* @Desc
* @date 2021/3/28 23:00
*/
public class TCallableCache {
public static Cache<String,String> cache = CacheBuilder.newBuilder().maximumSize(10)
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
System.out.println("过期移除:key="+removalNotification.getKey()+"--value="+
removalNotification.getValue()+"--移除原因="+removalNotification.getCause());
}
})
.build();
public static void main(String[] args) {
try {
for (int i = 0; i <20 ; i++) {
int vals = i;
String value = cache.get(i + "", new Callable() {
@Override
public Object call() throws Exception {
//未根据key 找到缓存,就设置缓存
String va = "设置缓存值"+ vals;
return va;
}
});
System.out.println("key="+i+"----对应的value="+value);
System.out.println("=============");
//主动删除缓存
cache.invalidate("18");
String tet = cache.getIfPresent("18");
System.out.println("18=="+tet);
}
}catch (Exception e){
e.printStackTrace();
}
}
}