含义
LRU,全程Least Recently Used, 最近最少使用的,也是最近最久没有使用的意思。一般用在内存淘汰策略里。如Redis的内存淘汰机制。
设计原则
如果一个内存最近一直没有使用,那么它的访问概率就会很低,当内存空间快满的时候,就应该把这些最近最近很久没有反问的数据给淘汰掉。
工作原理
-
操作系统教程里的LRU工作原理:页置换算法。
该算法的思路是,发生缺页中断时,选择未使用时间最长的页面置换出去
-
假如内存按照栈的方式访问,栈顶是最远使用的,栈底是最近使用的,满足栈的先进后出原则。
-
分别往栈插入1、2、3,当插入4已满,而且大于4就要开始淘汰最近很久没有使用的数据。所以插入5,就要开始从最底层删除3,
顶层插入5,变成第三个步骤的5、4、3、2,其他以次类推。但下图的最后插入5时,因为之前存在5,所以直接把原来的给删除后,再从顶层插入5就不一样。具体可以参考下图。
实现方式
-
数组
插入的数据存放到数组里,并且插入的元素标记个访问的时间戳,每次插入数组元素时,自己的时间戳为0,其他元素的时间戳+1,
当访问元素时,被访问元素的时间戳置为0. 插入、删除的时间复杂度都是O(n)。
-
链表
每次插入的数据存放到链表的头部,每次访问且命中数据,则移到头部,但链表容量满的时候,移除链表尾部数据。时间复杂度O(n)。
-
双链表+HashMap
-
访问和插入方式和链表一样,只是Map的Value指向的是链表的Node点的V,这样可以快速定位,时间复杂度O(1)。
LinkedHashMap是最适合实现LRU的数据结构。HashMap有个抽象方法removeEldestEntry方法,本身没有任何实现,按LinedHashMap重写了这个方法(如下)
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
这样,我们可以自己重写这个方法,定义淘汰策略。
-
JAVA代码如下:https://github.com/zhanshenzhi2008/orjrs_jdk/blob/master/src/main/java/com/orjrs/jdk/lru/LinkedHashMapLru.java
package com.orjrs.jdk.lru; import java.util.LinkedHashMap; import java.util.Map; /** * 线性列表实现LRU(Least Recently Used) * * @author orjrs * @create 2020-05-24 08:49 * @since 1.0.0 */ public class LinkedHashMapLru<K, V> { /** 双链表HashMap */ private LinkedHashMap<K, V> linkedHashMap; /** map */ private static final float THRSHOLD = 0.75f; /** 缓存大小 */ private int cacheSize; public void LinkedLru(int cacheSize) { int capacity = (int) Math.ceil(cacheSize * THRSHOLD); linkedHashMap = new LinkedHashMap<K, V>(capacity, THRSHOLD, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > LinkedHashMapLru.this.cacheSize; } }; } }