首页 > 其他分享 >【揭秘】如何使用LinkedHashMap来实现一个LUR缓存?

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存?

时间:2024-01-01 23:32:23浏览次数:27  
标签:LUR 缓存 链表 访问 LRU 数据 LinkedHashMap

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存? - 程序员古德

LRU(Least Recently Used)缓存是一种常用的缓存淘汰策略,用于在有限的缓存空间中存储数据。其基本思想是:如果数据最近被访问过,那么在未来它被访问的概率也更高。因此,LRU缓存会保留最近访问过的数据,并在缓存满时淘汰最久未使用的数据

定义

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存? - 程序员古德

LRU(Least Recently Used)缓存是一种常用的缓存淘汰策略,用于在有限的缓存空间中存储数据,其基本思想是,如果数据最近被访问过,那么在未来它被访问的概率也更高,因此,LRU缓存会保留最近访问过的数据,并在缓存满时淘汰最久未使用的数据,代码实现思路如下:

  1. 插入新的数据项。
  2. 访问(或检索)现有的数据项,并将其标记为最近使用。
  3. 当缓存达到其容量限制时,删除最久未使用的数据项。

代码案例

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存? - 程序员古德

为了演示LRU,使用LinkedHashMap类来实现一个LUR缓存,因为它内部已经处理了哈希表和双向链表,哈希表提供了快速的插入和查找操作(平均时间复杂度为O(1)),而双向链表则维护了元素的插入顺序或访问顺序(取决于构造函数的参数),代码如下:

import java.util.LinkedHashMap;  
import java.util.Map;  
  
public class LRUCache<K, V> extends LinkedHashMap<K, V> {  
    private int cacheSize;  
  
    public LRUCache(int cacheSize) {  
        // 第三个参数设置为true表示应该按照访问顺序排序,最近访问的放在头部,最老访问的放在尾部  
        super(16, 0.75f, true); // 可以使用一个默认的初始容量,例如16  	
        this.cacheSize = cacheSize;  
    }  
  
    @Override  
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {  
        // 当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据(即尾部的数据)  
        return size() > cacheSize;  
    }  
}

在上面的代码中,LRUCache类继承了LinkedHashMap并重写了removeEldestEntry方法,这个方法的默认实现总是返回false,意味着不会自动移除最老的条目,但是在实现中,当缓存的大小超过了指定的cacheSize时,该方法返回true,触发移除最久未使用的条目(也就是链表中的尾部元素)。

此外,通过将LinkedHashMap的构造函数中的accessOrder参数设置为true,让链表按照访问顺序来排序元素,这样,最近访问的元素会被放在链表的头部,而最久未访问的元素则会被放在尾部,当需要移除元素时,可以快速地移除链表尾部的元素。

这个设计思想利用了哈希表的高效查找和链表的顺序性来实现一个简单而有效的LRU缓存,通过重写一个方法,能够定制缓存的行为以符合LRU策略。

public static void main(String[] args) {  
    LRUCache<Integer, String> lruCache = new LRUCache<>(3);  
    lruCache.put(1, "one");    // 缓存是 {1="one"}  
    lruCache.put(2, "two");    // 缓存是 {1="one", 2="two"}  
    lruCache.put(3, "three");  // 缓存是 {1="one", 2="two", 3="three"}  
    lruCache.get(1);           // 最近访问的是1,缓存是 {2="two", 3="three", 1="one"}  
    lruCache.put(4, "four");   // 因为缓存容量只有3,所以移除最老的条目2,缓存变为 {3="three", 1="one", 4="four"}  
}

核心总结

【揭秘】如何使用LinkedHashMap来实现一个LUR缓存? - 程序员古德

使用LinkedHashMap实现LRU非常简单且高效,当业务比较简单、或者用来演示LRU的实现是没有啥问题的,它本身有一些限制,因此不适合用在线上,如下:

1、它不是线程安全的,如果多个线程同时访问这个LRU缓存,可能会导致数据不一致的问题,根本在于LinkedHashMap本身并不是线程安全的,所以在多线程环境下,需要额外的同步措施,比如使用Collections.synchronizedMap方法来包装这个缓存,或者在访问时加上同步块。

2、它没办法自动处理数据加载以及数据过期,在实际应用中,有时希望当缓存中不存在请求的数据时能够自动从数据库或其他数据源加载数据,或者当数据在一定时间内没有被访问时能够自动过期。

3、它没办法精准控制内存使用,虽然可以限制缓存中的条目数量,但是这个限制并不直接对应于内存使用量,不同的缓存条目可能占用不同大小的内存,所以这个简单的LRU缓存可能会导致内存溢出,尤其是在存储大对象时。

4、它很难扩展,由于这个实现是基于LinkedHashMap的,它的扩展性受到了一定的限制,如果需要更复杂的缓存行为或更高级的功能(比如缓存分区、备份、持久化等),它是做不到的。

关注我,每天学习互联网编程技术 - 程序员古德

标签:LUR,缓存,链表,访问,LRU,数据,LinkedHashMap
From: https://blog.51cto.com/bytegood/9058687

相关文章

  • Spring如何利用三级缓存解决单例Bean的循环依赖
    循环依赖:就是N个类循环(嵌套)引用。通俗的讲就是N个Bean互相引用对方,最终形成闭环。用一幅经典的图示可以表示成这样(A、B、C都代表对象,虚线代表引用关系):注意:其实可以N=1,也就是极限情况的循环依赖:自己依赖自己可以设想一下这个场景:如果在日常开发中我们用new对象的方式,若构造......
  • 07PCIE数据卡BRAM缓存中断采集
    软件版本:vitis2021.1(vivado2021.1)操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!7.1概述在方案中,使用基于AXI4实现的FDMA来实现数据的缓存。通过切换缓存的地址,实现2帧以上缓存数据的读取。这种构架......
  • 08PCIE数据卡DDR缓存中断采集
    软件版本:vitis2021.1(vivado2021.1)操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!8.1概述上一个例子演示了用BRAM作为数据缓存,显然板卡的BRAM容量非常有限,如果需要更大量数据的缓存就得用到DDR作为缓......
  • .Net Core WebAPI 缓存
    Asp.NetCoreWebAPI缓存 一、缓存缓存指在中间层中存储数据的行为,该行为可使后续数据检索更快。从概念上讲,缓存是一种性能优化策略和设计考虑因素。缓存可以显著提高应用性能,方法是提高不常更改(或检索成本高)的数据的就绪性。二、RFC9111在最新的缓存控制规范文件RFC91......
  • Java+SpringBoot+Maven+TestNG+httpclient+Allure+Jenkins实现接口自动化
    一、方案需求目标:测试左移,测试介入研发过程,验证单接口正常及异常逻辑选用工具:Java、SpringBoot、Maven、TestNG、httpclient、Allure、Jenkins方案:创建测试接口测试工程,参照研发设计文档和设计思路,编写正常及异常用例,直接调用服务端接口,覆盖接口逻辑和验证异常处理,提升接口健壮......
  • Python+Selenium+Pytest+Allure+Jenkins实现的Web自动化框架
    目录一、测试的项目二、需求分析三、用例设计-部分用例举例四、框架说明4.1测试框架结构图如下:4.2项目功能五、代码设计与功能说明5.1POM简介:PageObjectModle页面对象模型5.2基础封装层:pages/basePage.py5.3PO页面对象层:pages/userLoginPage.py5.4TestCase测试用例层:testc......
  • Apache Commons JCS缓存解决方案
    第1章:引言大家好,我是小黑!今天,咱们来聊聊ApacheCommonsJCS,一个Java界里的缓存大杀器。缓存技术,对于提高应用性能来说,就像是给它加了一剂兴奋剂,能让数据访问变得快如闪电。而ApacheCommonsJCS,作为一个开源的Java缓存框架,它的出现就像是给了咱们一个超级工具箱,不仅强大而且使用......
  • 【flink番外篇】4、flink的sink(内置、mysql、kafka、redis、clickhouse、分布式缓存、
    文章目录Flink系列文章一、maven依赖二、广播变量BroadcastVariables示例1、介绍2、广播变量示例3、验证三、BroadcastState与BroadcastVariable区别本文简单的介绍了flink中关于广播变量的简单使用示例。一、maven依赖为避免篇幅过长,所有基础依赖均在第一篇文章中列出,具......
  • 【flink番外篇】4、flink的sink(内置、mysql、kafka、redis、clickhouse、分布式缓存、
    文章目录Flink系列文章一、maven依赖二、Flinksink介绍三、sink到文件、console示例1、console输出2、sink到文件1)、sinktxt文件到hdfs上2)、sinkcsv文件到本地3)、sinktext文件到hdfs上(writeUsingOutputFormat)四、sink到socket示例(writeToSocket)五、Jdbc/mysql示例1、maven依......
  • 【flink番外篇】4、flink的sink(内置、mysql、kafka、redis、clickhouse、分布式缓存、
    文章目录Flink系列文章一、maven依赖二、分布式缓存(DistributedCache)示例1、介绍2、maven依赖3、实现4、验证1)、验证步骤2)、验证本文介绍了flink关于分布式缓存的使用示例,比较简单。本文除了maven依赖外,没有其他依赖。本示例需要hadoop环境可用。一、maven依赖为避免篇幅过长,所......