在开发应用中,避免不了会使用到缓存,分布式缓存一般会用到redis、Memcache等常用,本地缓存像ehcache这种用的也是很多
今天介绍下google guava框架插件提供的LoadingCache本地缓存,LoadingCache和ConcurrentMap差不多,都是线程安全的,只不过比ConcurrentMap多了一些其他的功能,如过期策略等
优点
1、线程安全的缓存,与ConcurrentMap相似,但前者增加了更多的元素失效策略,后者只能显示的移除元素。
2、提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。定时回收有两种:按照写入时间,最早写入的最先回收;按照访问时间,最早访问的最早回收。
3、监控缓存加载/命中情况。
4、集成了多部操作,调用get方式,可以在未命中缓存的时候,从其他地方获取数据源(DB,redis),并加载到缓存中
随手简单写的一个类(有待改进)
package com.shentb.hmb.cache;
import com.google.common.cache.*;
import com.shentb.hmb.service.impl.LoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public final class LoadingLocalCache {
private static LoginService loginService;
private static Logger logger = LoggerFactory.getLogger(LoadingLocalCache.class);
private static LoadingCache<String, Object> loadingCache;
private static LoadingLocalCache loadingLocalCache;
private static int DEFAULT_CONCURRENCY_LEVEL= 4;
private static int DEFAULT_EXPIRE= 1800;
private static int DEFAULT_INITIAL_CAPACITY= 16;
private static int DEFAULT_MAXIMUM_SIZE= 100;
private static TimeUnit DEFAULT_TIME_UNIT= TimeUnit.SECONDS;
@Autowired
public void setLoginService(LoginService loginService) {
LoadingLocalCache.loginService = loginService;
}
static {
init();
}
private LoadingLocalCache(){}
protected static void init() {
loadingCache= CacheBuilder
.newBuilder()
//并发级别(同时写入缓存的线程数)
.concurrencyLevel(DEFAULT_CONCURRENCY_LEVEL)
//过期时间
.expireAfterWrite(DEFAULT_EXPIRE,DEFAULT_TIME_UNIT)
//初始容量
.initialCapacity(DEFAULT_INITIAL_CAPACITY)
//最大容量,超过之后默认按照LRU算法最近使用最少移除缓存项
.maximumSize(DEFAULT_MAXIMUM_SIZE)
//设置统计缓存的命中率
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
logger.info(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(
new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return loginService.selectByPhoneNo(key);
}
}
);
}
public static Object put(String key, Object value){
loadingCache.put(key, value);
return value;
}
public static Object get(String key) throws Exception {
return loadingCache.get(key);
}
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
}
return reference;
}
// System.out.println(sellerContactVOCache.stats().toString());
}
常用方法:
get(K):这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地loading新值(就是上面说的当缓存没有值的时候执行Load方法)
put(key, value):这个方法可以直接显示地向缓存中插入值,这会直接覆盖掉已有键之前映射的值。
缓存回收:
CacheBuilder.maximumSize(long):这个方法规定缓存项的数目不超过固定值(其实你可以理解为一个Map的最大容量),尝试回收最近没有使用或总体上很少使用的缓存项
定时回收(Timed Eviction):
expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
显式清除:
任何时候,你都可以显式地清除缓存项,而不是等到它被回收:
个别清除:Cache.invalidate(key) 批量清除:Cache.invalidateAll(keys) 清除所有缓存项:Cache.invalidateAll()
移除监听器
通过CacheBuilder.removalListener(RemovalListener),可以声明一个监听器,以便缓存项被移除时做一些额外操作。缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification],其中包含移除原因[RemovalCause]、键和值