含義
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; } }; } }