fifo算法_自己也可以开发缓存实现(LRU,LFU,FIFO)

在学操作系统的时候,会接触到页面缓存调度算法。缓存不可能是无限大的,所以会涉及到一些置换策略,来保证缓存的命中率。常见的有:FIFO、LRU、LFU策略等。

93c058be13ee8a99f23427a094cd93a9.png

缓存置换算法

1、LRU(Least Recently Used ,最近最少使用)

算法根据数据的最近访问记录来淘汰数据,其原理是如果数据最近被访问过,将来被访问的几概率相对比较高,最常见的实现是使用一个链表保存缓存数据,详细具体算法如下:

  • 1. 新数据插入到链表头部;
  • 2. 每当缓存数据命中,则将数据移到链表头部;
  • 3. 当链表满的时候,将链表尾部的数据丢弃;

2、LFU(Least Frequently Used,最不经常使用)

算法根据数据的历史访问频率来淘汰数据,其原理是如果数据过去被访问次数越多,将来被访问的几概率相对比较高。LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。

具体算法如下:

  • 1. 新加入数据插入到队列尾部(因为引用计数为1);
  • 2. 队列中的数据被访问后,引用计数增加,队列重新排序;
  • 3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除;

3、FIFO(First In First Out ,先进先出)

算法是根据先进先出原理来淘汰数据的,实现上是最简单的一种,具体算法如下:

  • 1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;
  • 2. 淘汰FIFO队列头部的数据;

缓存置换算法的要素

(1)缓存不是无限大,需要有一个固定的大小来约束其大小

(2)缓存满后,再次插入需要替换掉某些元素,才能添加新元素

(3)每次访问完缓存,可能需要改变缓存元素的状态,如元素顺序的改变

通过LinkedHashMap理解LRU和FIFO算法

先看下LinkedHashMap的构造函数,三个参数分别为:初始化大小、装载因子和访问顺序。

- 当参数accessOrder = true时,则按照访问顺序对Map排序,那么调用get()方法后,会将这次访问的元素移至链表尾部。不断访问可以形成按访问顺序排序的链表。

- 当参数accessOrder = false时,则按照插入顺序对Map排序。先插入的元素放置在链表的首部,按照尾插入的方式维护链表。

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }

根据链表中元素的顺序可以分为:按插入顺序的链表(默认,false),和按访问顺序的链表(调用get方法)。默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。 可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素。

结论1

可以得出accessOrder = true时,可以模仿出LRU的特性;accessOrder = false时,可以模仿出FIFO的特性。即满足缓存置换算法要素3。

这是LinkedHashMap中另外一个方法:移除最久的元素。当返回为false时,不会移除其中最久的元素。当返回true的时候,就会remove其中最久的元素。

protected boolean removeEldestEntry(Map.Entry eldest) { return false; }

结论2

由于缓存置换算法要素2,当缓存满了之后,需通过返回true删除最久未被使用的元素。所以我们需要 重写此方法来删除缓存元素,达到缓存置换的要求。

当然,我们还需要满足缓存置换算法要素1,就大功告成了。由于Java Map是自动扩容的,当其table.size() > Capacity * loadFactor的时,会自动进行两倍扩容。

结论

为了使缓存能固定大小,需要禁止Map的自动扩容。可将初始化大小设置为(cacheSize / loadFactor) + 1,就可以在元素数目达到缓存大小时,不会自动扩容,达到缓存置换的要求。

自己实现简单的FIFO缓存

1、继承继承LinkedHashMap

3135aa3bca98a71f2338fb404bf24d96.png

2、根据LinkedHashMap重新实现

由于LinkedHashMap并非是线程安全的,我们可以仅利用LinkedHashMap的特性自己实现一个。

9f014d358d93e847cff9b52b1ed06eb6.png

三、实现简单的LRU缓存

1、继承LinkedHashMap

和FIFO的实现基本一致,只需要将accessOrder = false。

4025ab4a45b993aa004cebdc86e0d772.png

2、根据LinkedHashMap重新实现

同样,由于LinkedHashMap并非是线程安全的,我们可以仅利用LinkedHashMap的特性自己实现一个。

和FIFO的实现基本一致,只需要将accessOrder = false。

0ba4296ddff705279f04821a71b4cc76.png
参考文档:https://blog.csdn.net/f59130/article/details/80955366
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值