当前位置:   article > 正文

缓存置换算法 - LRU算法_内存替换算法lru

内存替换算法lru

LRU算法

1 原理

对于在内存中并且不被使用的数据块就是LRU,这类数据需要从内存中删除,以腾出空间来存储常用的数据。

LRU算法(Least Recently Used,最近最少使用),是内存管理的一种页面置换算法,就是用来删除内存中不被使用的数据,腾出空间来把常用的数据存进去。

LRU算法的实现原理:把所有的缓存数据存入链表中,新插入的或被访问的数据存入链表头,如果链表满了,就把尾部的数据清除。如下图所示:

比如长度是5,缓存中的数据是:1,2,3,4,5

访问了2,那么缓存中的数据顺序变为:2,1,3,4,5

新插入了6,会变为:6,2,1,3,4

2 Java编码实现

2.1 基于linkedhashmap实现

linkedhashmap的底层实现是链表,使用linkedhashmap实现LRU算法的关键在于设置链表的长度,代码如下所示:

  1. import java.util.LinkedHashMap;
  2. import java.util.Map;
  3. /**
  4. * @ClassName LRU
  5. * @Description 基于LinkedHashMap的LRU算法
  6. * @Author boy
  7. * @Date 2019/6/13 10:27 AM
  8. */
  9. public class LRULinkedHashMap extends LinkedHashMap{
  10. //缓存长度
  11. int length;
  12. public LRULinkedHashMap(int length){
  13. super(length,0.75f,true);
  14. this.length = length;
  15. }
  16. /*
  17. * @Author boy
  18. * @Description LRU算法的关键方法,超过最大长度就移除尾部元素
  19. * @Date 2019/6/13 12:24 PM
  20. * @Param [map]
  21. * @return boolean
  22. */
  23. @Override
  24. public boolean removeEldestEntry(Map.Entry map){
  25. return size()>length;
  26. }
  27. }

2.2 链表实现

可以自己维护一个链表,来实现LRU算法,但是这种方式的弊端是查询、插入的效率比较慢,代码实现如下所示:

  1. /**
  2. * @ClassName LRU
  3. * @Description
  4. * @Author boy
  5. * @Date 2019/6/20 8:19 PM
  6. */
  7. public class LRU{
  8. //链表的头结点
  9. Node head;
  10. //链表的尾结点
  11. Node tail;
  12. //长度
  13. int length;
  14. public LRU(int length){
  15. this.length = length;
  16. }
  17. public void addNode(String value){
  18. Node node = new Node();
  19. node.value = value;
  20. if (null == head){
  21. head = node;
  22. tail = node;
  23. return;
  24. }
  25. if (length<=size()){
  26. delTailNode();
  27. }
  28. head.prev = node;
  29. node.next = head;
  30. head = node;
  31. }
  32. public Node getNode(String value){
  33. Node node = head;
  34. while (node!=null){
  35. if (value.equals(node.value)){
  36. Node preNode = node.prev;
  37. preNode.next = node.next;
  38. node.next.prev = preNode;
  39. node.next = head;
  40. head.prev = node;
  41. head = node;
  42. break;
  43. }else {
  44. node = node.next;
  45. }
  46. }
  47. return node;
  48. }
  49. public void delTailNode(){
  50. tail = tail.prev;
  51. tail.next = null;
  52. }
  53. public void delNode(String value){
  54. Node node = head;
  55. while (node!=null){
  56. if (value.equals(node.value)){
  57. Node comNode = node.prev;
  58. comNode.next = node.next;
  59. node.next.prev = comNode;
  60. break;
  61. }else {
  62. node = node.next;
  63. }
  64. }
  65. }
  66. public int size(){
  67. int length = 0;
  68. Node node = head;
  69. while (node!=null){
  70. length++;
  71. node = node.next;
  72. }
  73. return length;
  74. }
  75. }
  76. class Node{
  77. //节点值
  78. String value;
  79. //指向上一个节点的引用
  80. Node prev;
  81. //指向下一个节点的引用
  82. Node next;
  83. }

2.3 链表+HashTable实现

解决自己维护链表,插入、查询慢的问题,可以使用链表+HashTable来实现LRU算法,代码如下所示:

  1. import java.util.Hashtable;
  2. /**
  3. * @ClassName LRUCache
  4. * @Description 链表+HashTable实现LRU算法
  5. * @Author boy
  6. * @Date 2019/6/21 8:50 PM
  7. */
  8. public class LRUCache {
  9. private int cacheSize;
  10. private Hashtable nodes;//缓存容器
  11. private int currentSize;
  12. private CacheNode first;//链表头
  13. private CacheNode last;//链表尾
  14. public LRUCache(int i) {
  15. currentSize = 0;
  16. cacheSize = i;
  17. nodes = new Hashtable(i);//缓存容器
  18. }
  19. /**
  20. * 获取缓存中对象
  21. * @param key
  22. * @return
  23. */
  24. public Object get(Object key) {
  25. CacheNode node = (CacheNode) nodes.get(key);
  26. if (node != null) {
  27. moveToHead(node);
  28. return node.value;
  29. } else {
  30. return null;
  31. }
  32. }
  33. /**
  34. * 添加缓存
  35. * @param key
  36. * @param value
  37. */
  38. public void put(Object key, Object value) {
  39. CacheNode node = (CacheNode) nodes.get(key);
  40. if (node == null) {
  41. //缓存容器是否已经超过大小.
  42. if (currentSize >= cacheSize) {
  43. if (last != null)//将最少使用的删除
  44. nodes.remove(last.key);
  45. removeLast();
  46. } else {
  47. currentSize++;
  48. }
  49. node = new CacheNode();
  50. }
  51. node.value = value;
  52. node.key = key;
  53. //将最新使用的节点放到链表头,表示最新使用的.
  54. moveToHead(node);
  55. nodes.put(key, node);
  56. }
  57. /**
  58. * 将缓存删除
  59. * @param key
  60. * @return
  61. */
  62. public Object remove(Object key) {
  63. CacheNode node = (CacheNode) nodes.get(key);
  64. if (node != null) {
  65. if (node.prev != null) {
  66. node.prev.next = node.next;
  67. }
  68. if (node.next != null) {
  69. node.next.prev = node.prev;
  70. }
  71. if (last == node)
  72. last = node.prev;
  73. if (first == node)
  74. first = node.next;
  75. }
  76. return node;
  77. }
  78. public void clear() {
  79. first = null;
  80. last = null;
  81. }
  82. /**
  83. * 删除链表尾部节点
  84. * 表示 删除最少使用的缓存对象
  85. */
  86. private void removeLast() {
  87. //链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
  88. if (last != null) {
  89. if (last.prev != null)
  90. last.prev.next = null;
  91. else
  92. first = null;
  93. last = last.prev;
  94. }
  95. }
  96. /**
  97. * 移动到链表头,表示这个节点是最新使用过的
  98. * @param node
  99. */
  100. private void moveToHead(CacheNode node) {
  101. if (node == first)
  102. return;
  103. if (node.prev != null)
  104. node.prev.next = node.next;
  105. if (node.next != null)
  106. node.next.prev = node.prev;
  107. if (last == node)
  108. last = node.prev;
  109. if (first != null) {
  110. node.next = first;
  111. first.prev = node;
  112. }
  113. first = node;
  114. node.prev = null;
  115. if (last == null)
  116. last = first;
  117. }
  118. }
  119. /**
  120. * 链表节点
  121. * @author Administrator
  122. *
  123. */
  124. class CacheNode {
  125. CacheNode prev;//前一节点
  126. CacheNode next;//后一节点
  127. Object value;//
  128. Object key;//
  129. CacheNode() {
  130. }
  131. }

LRU算法的演变

LRU-K算法

LRU算法的弊端是会产生“缓存污染”,比如周期性访问的数据。为了解决这个问题,我们引入LRU-K算法。举个例子,需要查询的数据特别频繁,我们从redis或者数据库中拉取数据比较慢可以放在jvm缓存中,如下图所示:

满足条件的数据放在LRU队列中,应用程序第一次拉取数据从LRU队列中来拿,如果没有再从历史队列中拿,如果还拿不到就从redis中拿,并把拿到的数据放入历史队列中,历史队列的数据满足一定条件就放入LRU队列中

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/163702
推荐阅读
相关标签
  

闽ICP备14008679号