废话不多说, 直接上代码:
代码
/**
* @author : Alvin
* @date : 2022/06/17 10:38
* @desc : 本地缓存
* 淘汰策略:
* 1. 数据超过过期时间立即失效
* 2. 加入缓存超过最大容量时, 淘汰最不活跃(最后使用到现在时间最长)的数据.
*/
@Slf4j
public class LocalCache<K, V> implements Map<K, V> {
/**
* 缓存默认最大容量: 4096
*/
private static final int DEFAULT_MAX_CAPACITY = 1 << 12;
/**
* 定期全面清理过期数据的间隔时间: 1分钟
*/
private static final int ONE_MINUTE = 60 * 1000;
/**
* 最大容量
*/
private int maxCapacity;
/**
* 默认过期时间(毫秒), -1 表示不过期.
*/
private long defaultTtlTime = -1;
/**
* 缓存使用频率
*/
private LinkedList<K> cacheList = new LinkedList<>();
/**
* 缓存对象
*/
private ConcurrentHashMap<K, CacheValue<V>> cacheMap;
public LocalCache() {
this(DEFAULT_MAX_CAPACITY);
}
/**
* @param maxCapacity 最大容量
*/
public LocalCache(int maxCapacity) {
this(maxCapacity, -1L);
}
/**
* @param maxCapacity 最大容量
* @param defaultTtlTime 默认过期时间(毫秒), -1 表示不过期.
*/
public LocalCache(int maxCapacity, long defaultTtlTime) {
this.maxCapacity = maxCapacity;
if (defaultTtlTime > 0) {
this.defaultTtlTime = defaultTtlTime;
}
cacheMap = new ConcurrentHashMap<>();
Thread thread = new Thread(this::cleanUpExpired, "clear_thread");
thread.setDaemon(true);
thread.start();
}
/**
* 返回缓存中有效键值的数量
*/
@Override
public int size() {
return entrySet().size();
}
/**
* 返回缓存中是否有 有效键值数据
*/
@Override
public boolean isEmpty() {
return entrySet().size() == 0;
}
/**
* 返回缓存中是否有 有效的此键
*/
@Override
public boolean containsKey(Object key) {
CacheValue<V> cacheValue = cacheMap.get(key);
if (cacheValue != null) {
if (cacheValue.ttlTime == -1 || cacheValue.ttlTime > System.currentTimeMillis()) {
return true;
}
remove(key);
return false;
}
return false;
}
/**
* 返回缓存中是否有 有效的此值
*/
@Override
public boolean containsValue(Object value) {
long now = System.currentTimeMillis();
for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
CacheValue<V> cacheValue = entry.getValue();
if (Objects.equals(cacheValue.value, value)) {
if (cacheValue.ttlTime > now) {
return true;
}
remove(entry.getKey());
return false;
}
}
return false;
}
/**
* 从缓存中获取有效的值
*/
@Override
public V get(Object key) {
CacheValue<V> cacheValue = cacheMap.get(key);
if (cacheValue != null) {
cacheList.remove(key);
if (cacheValue.ttlTime > System.currentTimeMillis()) {
cacheList.addLast((K) key);
return cacheValue.value;
}
cacheMap.remove(key);
}
return null;
}
/**
* 从缓存中获取有效的值, 如果不存在, 则缓存默认值, 并返回
*
* @param key 键
* @param defaultV 默认值
*/
public V get(K key, V defaultV) {
return get(key, defaultV, defaultTtlTime);
}
/**
* 从缓存中获取有效的值, 如果不存在, 则缓存默认值, 并返回
*
* @param key 键
* @param defaultV 默认值
* @param ttlTime 缓存默认值的有效时间(毫秒)
*/
public V get(K key, V defaultV, long ttlTime) {
V v = get(key);
if (v != null) {
return v;
}
put(key, defaultV, ttlTime);
return defaultV;
}
/**
* 从缓存中获取有效的值, 如果不存在, 则缓存方法的返回值, 并返回
*
* @param key 键
* @param function 提供值的方法
*/
public V get(K key, Function<K, V> function) {
return get(key, function, defaultTtlTime);
}
/**
* 如果不存在, 则缓存方法的返回值, 并返回
*
* @param key 键
* @param function 提供值的方法
* @param ttlTime 缓存方法返回值的有效时间(毫秒)
*/
public V get(K key, Function<K, V> function, long ttlTime) {
V v = get(key);
if (v != null) {
return v;
}
V value = function.apply(key);
put(key, value, ttlTime);
return value;
}
/**
* 缓存数据
*/
@Override
public V put(K key, V value) {
return put(key, value, defaultTtlTime);
}
/**
* 缓存数据
*
* @param key 键
* @param value 值
* @param ttlTime 过期时间(毫秒), -1 表示不过期.
*/
public V put(K key, V value, long ttlTime) {
if (ttlTime == 0) {
return null;
} else if (ttlTime < 0) {
ttlTime = -1L;
} else {
ttlTime = System.currentTimeMillis() + ttlTime;
}
if (cacheMap.containsKey(key)) {
remove(key);
} else if (cacheList.size() >= maxCapacity) {
cleanUpExpired();
if (cacheList.size() >= maxCapacity) {
synchronized (this) {
cacheMap.remove(cacheList.removeFirst());
}
}
}
CacheValue<V> cacheValue = new CacheValue<>(value, ttlTime);
synchronized (this) {
cacheList.addLast(key);
cacheMap.put(key, cacheValue);
}
return value;
}
/**
* 设置 key 缓存时间
*
* @param key 键
* @param ttlTime 过期时间(毫秒), -1 表示不过期.
*/
public boolean expire(K key, long ttlTime) {
long now = System.currentTimeMillis();
if (ttlTime < 0) {
ttlTime = -1L;
} else {
ttlTime = now + ttlTime;
}
CacheValue<V> cacheValue = cacheMap.get(key);
if (cacheValue != null) {
if (cacheValue.ttlTime == -1 || cacheValue.ttlTime > now) {
cacheValue.ttlTime = ttlTime;
return true;
}
remove(key);
return false;
}
return false;
}
/**
* 删除缓存值
*/
@Override
public synchronized V remove(Object key) {
CacheValue<V> remove = cacheMap.remove(key);
if (remove != null) {
cacheList.remove(key);
return remove.value;
}
return null;
}
/**
* 全部增加进缓存
*/
@Override
public synchronized void putAll(Map<? extends K, ? extends V> m) {
if (m.size() > 0) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
this.put(entry.getKey(), entry.getValue());
}
}
}
/**
* 清空缓存
*/
@Override
public synchronized void clear() {
cacheMap.clear();
cacheList.clear();
}
/**
* 有效值的键
*/
@Override
public Set<K> keySet() {
Set<K> set = new HashSet<>();
long now = System.currentTimeMillis();
if (cacheMap.size() > 0) {
for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
if (entry.getValue().ttlTime > now) {
set.add(entry.getKey());
} else {
remove(entry.getKey());
}
}
}
return set;
}
/**
* 有效值
*/
@Override
public Collection<V> values() {
ArrayList<V> vs = new ArrayList<>();
long now = System.currentTimeMillis();
if (cacheMap.size() > 0) {
for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
CacheValue<V> value = entry.getValue();
if (value.ttlTime > now) {
vs.add(value.value);
} else {
remove(entry.getKey());
}
}
}
return vs;
}
/**
* 有效值的键值对
*/
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> set = new LinkedHashSet<>();
long now = System.currentTimeMillis();
for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
if (entry.getValue().ttlTime > now) {
Entry<K, V> cacheEntry = new CacheEntry<>(entry.getKey(), entry.getValue().value);
set.add(cacheEntry);
} else {
remove(entry.getKey());
}
}
return set;
}
/**
* 定期全面清理过期数据.
*/
private void cleanUpExpired() {
long last = System.currentTimeMillis();
while (true) {
long now = System.currentTimeMillis();
while (now - ONE_MINUTE < last) {
Thread.yield();
now = System.currentTimeMillis();
}
for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
CacheValue<V> value = entry.getValue();
if (value.ttlTime <= now) {
synchronized (this) {
cacheMap.remove(entry.getKey());
cacheList.remove(entry.getKey());
}
}
}
last = now;
}
}
@AllArgsConstructor
static class CacheValue<V> {
/**
* 缓存对象
*/
private V value;
/**
* 缓存过期时间
*/
private long ttlTime;
}
@AllArgsConstructor
static class CacheEntry<K, V> implements Map.Entry<K, V> {
private K k;
private V v;
@Override
public K getKey() {
return k;
}
@Override
public V getValue() {
return v;
}
@Override
public V setValue(V value) {
return this.v = value;
}
}
}
测试
public static void main(String[] args) {
LocalCache<String, Object> cache = new LocalCache<>();
// 缓存 java, 设置过期时间
cache.put("我爱", "java", 1000);
// 获取 "我爱" 的值.
Object iLove1 = cache.get("我爱");
System.out.println("iLove1 = " + iLove1);
ThreadUtil.sleep(1000);
// 再次获取 "我爱" 的值.
Object iLove2 = cache.get("我爱");
System.out.println("iLove2 = " + iLove2);
// 没有缓存 "你也爱", 直接获取.
Object uLove1 = cache.get("你也爱", (k) -> k + "java", 1000);
System.out.println("uLove1 = " + uLove1);
// 再次获取 "你也爱"
Object uLove2 = cache.get("你也爱");
System.out.println("uLove2 = " + uLove2);
}
测试结果
iLove1 = java
iLove2 = null
uLove1 = 你也爱java
uLove2 = 你也爱java
欢迎留言交流