本次介绍的本地内存缓存和ExpiryMap(之前写过这个博客)差不多。
具体介绍:
1)调用save()方法,可以添加缓存,并指定过期时间。
2)调用load()方法可以从本地缓存中读取数据,如果缓存过期了,则返回null,业务层需要从数据库等持久化存储库中查询数据,并再次缓存起来。
3)也可以调用clearCache()方法手动删除指定key缓存。
4)定时任务会每隔5分钟定时清理一次已过期的缓存数据。
工具类(可以直接拿来用)
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* 本地内存缓存工具类
*/
@Slf4j
public class LocalCacheUtil {
private LocalCacheUtil() {
}
// 定时任务执行器
private static ScheduledExecutorService refreshExecutor = new ScheduledThreadPoolExecutor(1, Executors.defaultThreadFactory());
// 当前缓存
private static volatile ConcurrentHashMap<String, CacheElement<Object>> cacheMap = new ConcurrentHashMap<>();
// 缓存对象
static class CacheElement<T extends Object> {
// 缓存值
private T value;
// 过期时间
private Long expire;
/**
* 有参数构造方法
*
* @param value 缓存的值
* @param timeout 过期时间。单位:ms
*/
public CacheElement(T value, Long timeout) {
this.value = value;
this.expire = System.currentTimeMillis() + timeout;
}
/**
* 判断缓存是否过期
*/
public boolean isOvertime() {
return expire < System.currentTimeMillis();
}
}
/**
* 读取本地缓存
*
* @param key 缓存key
* @param requireClass 缓存值对应class
*/
public static <T extends Object> T load(String key, Class<T> requireClass) {
CacheElement<Object> e = cacheMap.get(key);
//过期返回null
if (null == e || e.isOvertime()) {
return null;
}
return TypeUtils.cast(e.value, requireClass, ParserConfig.getGlobalInstance());
}
/**
* 保存至本地缓存
*
* @param key 缓存key
* @param value 缓存值
* @param timeout 过期时间。单位:ms
*/
public static <T extends Object> void save(String key, T value, Long timeout) {
cacheMap.put(key, new CacheElement<>(value, timeout));
}
/**
* 手动清除key对应的本地缓存
*/
public static void clearCache(String key) {
cacheMap.put(key, null);
}
/**
* 定时(每5分钟)清理一次缓存中过期的数据
*/
static {
refreshExecutor.scheduleAtFixedRate(() -> {
ConcurrentHashMap<String, CacheElement<Object>> newCache = new ConcurrentHashMap<>();
for (Map.Entry<String, CacheElement<Object>> e : cacheMap.entrySet()) {
// 丢弃已经过期的数据
if (e.getValue().isOvertime()) {
continue;
}
newCache.put(e.getKey(), e.getValue());
}
cacheMap = newCache;
}, 0, 5 * 60, TimeUnit.SECONDS);
}
}