如何设计一个牛逼的本地缓存!
猿天地
共 5024字,需浏览 11分钟
·
2020-11-19 11:03
转自:juejin.im/post/6844904003394863118
最近在看Mybatis的源码,刚好看到缓存这一块,Mybatis提供了一级缓存和二级缓存;一级缓存相对来说比较简单,功能比较齐全的是二级缓存,基本上满足了一个缓存该有的功能;当然如果拿来和专门的缓存框架如ehcache来对比可能稍有差距;本文我们将来整理一下实现一个本地缓存都应该需要考虑哪些东西。
考虑点
1.数据结构
2.对象上限
3.清除策略
4.过期时间
5.线程安全
6.简明的接口
7.是否持久化
8.阻塞机制
如何实现
1.数据结构
Map<Object, Object> cache = new ConcurrentHashMap<Object, Object>()Mybatis使用HashMap本身是非线程安全的,所以可以看到起内部使用了一个SynchronizedCache用来包装,保证线程的安全性;
2.对象上限
3.清除策略
LRU :Least Recently
Used的缩写最近最少使用,移除最长时间不被使用的对象;常见的使用LinkedHashMap来实现,也是很多本地缓存默认使用的策略;
FIFO :先进先出,按对象进入缓存的顺序来移除它们;常见使用队列Queue来实现;
LFU :Least Frequently
Used的缩写大概也是最近最少使用的意思,和LRU有点像;区别点在LRU的淘汰规则是基于访问时间,而LFU是基于访问次数的;可以通过HashMap并且记录访问次数来实现;
SOFT :软引用基于垃圾回收器状态和软引用规则移除对象;常见使用SoftReference来实现;
WEAK :弱引用更积极地基于垃圾收集器状态和弱引用规则移除对象;常见使用WeakReference来实现;
4.过期时间
被动删除 :每次进行get/put操作的时候都会检查一下当前key是否已经过期,如果过期则删除,类似如下代码:
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
}
5.线程安全
public synchronized void putObject(Object key, Object object) {
...省略...
}
@Override
public synchronized Object getObject(Object key) {
...省略...
}
6.简明的接口
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}
public interface Cache<K, V> {
V getIfPresent(@CompatibleWith("K") Object key);
V get(K key, Callable extends V> loader) throws ExecutionException;
ImmutableMapgetAllPresent(Iterable> keys) ;
void put(K key, V value);
void putAll(Map extends K, ? extends V> m);
void invalidate(@CompatibleWith("K") Object key);
void invalidateAll(Iterable> keys);
void invalidateAll();
long size();
CacheStats stats();
ConcurrentMapasMap() ;
void cleanUp();
}
7.是否持久化
diskPersistent="false" //是否持久化磁盘缓存
8.阻塞机制
public class Memoizerl<A, V> implements Computable<A, V> {
private final Map> cache = new ConcurrentHashMap>();
private final Computable c;
public Memoizerl(Computable c) {
this.c = c;
}
@Override
public V compute(A arg) throws InterruptedException, ExecutionException {
while (true) {
Futuref = cache.get(arg);
if (f == null) {
Callableeval = new Callable () {
@Override
public V call() throws Exception {
return c.compute(arg);
}
};
FutureTaskft = new FutureTask (eval);
f = cache.putIfAbsent(arg, ft);
if (f == null) {
f = ft;
ft.run();
}
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
}
}
}
}
}
总结
后台回复 学习资料 领取学习视频
如有收获,点个在看,诚挚感谢
评论