前言
随着近些年大型电商应用(例如京东、阿里巴巴等)的迅速发展,本地缓存和分布式缓存相关技术被大量的应用。那么这些缓存的核心算法都有哪些呢,上次给大家手写了FIFO先进先出算法的实现过程,感兴趣的可以通过手写缓存之FIFO先进先出算法来进行学习和回顾。那么今天给大家介绍的是Lru淘汰缓存算法。手写缓存之FIFO先进先出算法
一、Lru缓存淘汰算法是什么?
Lru算法是近些年来一些大型电商平台比较推荐的一种算法,其原理是在我们的缓存满了以后按照访问数量来进行淘汰,优先淘汰那些访问次数比较低的缓存数据,大家来看下面的示例图来加深理解。
大家可以从图中看出随着新数据的放入,没有被访问的数据或者访问次数较少的优先被淘汰掉。我们联想一下,当几个商品的数据被频繁访问,那么这些商品的数据就应该被一直保留在缓存中,当缓存满了要放入新的数据的时候,那么就会优先淘汰掉那些访问比较少的数据,来确保我们的缓存里面可以实现比较好的效果,最大程度的缓解数据库的压力。
二、实现步骤
这里和FIFO算法案例一样,先来写一个接口类,并做出实现此接口的实现类,在此基础上我们再去实现Lru算法。
1. 接口
代码如下(示例):
/**
* Cache规范定义
*/
public interface Cache {
void putObject(Object key,Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int size();
}
2. 接口实现类
代码如下(示例):
/**
* 简易Cache实现
* 1)存储结构:散列表(基于JAVA中的hashmap存储)
* 2)淘汰算法:没有(直到到内存溢出)
* 3)线程安全:否
* 4)缓存对对象的引用?强引用
* 5)对象获取:浅拷贝(获取对象地址)
*/
public class PerpetualCache implements Cache{
private HashMap<Object,Object> cache=new HashMap<>();
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return "PerpetualCache{" +
"cache=" + cache.toString() +
'}';
}
}
3. Lru淘汰算法实现
代码如下(示例):
/**
* 基于LRU算法为缓存添加数据淘汰策略。
*/
public class LruCache implements Cache{
/**关联Cache对象-找到可以存储数据的基层对象*/
private Cache cache;
/**定义Cache的最大容量*/
private int maxCap;
/**通过LinkedHashMap记录key的访问顺序*/
private LinkedHashMap<Object,Object> keyAccessOrders;
/**记录最近访问次数最少的key*/
private Object eldEstKey;
public LruCache(Cache cache, int maxCap) {
this.cache = cache;
this.maxCap = maxCap;
keyAccessOrders=new LinkedHashMap<Object,Object>(maxCap,0.75f,true){
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean isFull= size()>maxCap;
if(isFull)eldEstKey=eldest.getKey();//获取最近访问次数最少的key
return isFull;
}
};
}
@Override
public void putObject(Object key, Object value) {
//1.向Cache中添加新的元素
cache.putObject(key, value);
//2.记录key的访问顺序
keyAccessOrders.put(key, key);
//3.Cache满了则移除最近访问次数最少的key/value
if(eldEstKey!=null){
cache.removeObject(eldEstKey);
eldEstKey=null;
}
}
@Override
public Object getObject(Object key) {
//1.记录key的访问顺序
keyAccessOrders.get(key);
//2.返回cache中的指定key对应的value
return cache.getObject(key);
}
@Override
public Object removeObject(Object key) {
Object object = cache.removeObject(key);
keyAccessOrders.remove(key);
return object;
}
@Override
public void clear() {
cache.clear();
keyAccessOrders.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return "LruCache{" +
"cache=" + cache.toString() +
'}';
}
public static void main(String[] args) {
Cache cache=new LruCache(//负责添加算法
new PerpetualCache(),//负责存数据
3);
cache.putObject("A", 100);
cache.putObject("B", 200);
cache.putObject("C", 300);
cache.getObject("A");
cache.putObject("D", 400);
cache.putObject("E", 500);
System.out.println(cache);
}
}
4. 运行结果演示
期间我们访问了一次A数据 ,当放入D和E之后,没有被访问的B和C被淘汰了,A数据保存了下来并处于头的位置。
总结
通过以上的实现小案例,我们已经初步理解并实现了Lru淘汰算法。相对与FIFO先进先出算法更加智能化,所以被京东这些大型电商平台广泛应用,小伙伴们学会了吗?