设计并实现一个并发安全的LRU(Least Recently Used,最近最少使用)缓存结构

文章目录

    • 前言
    • 实战演示
    • 写在最后

前言

相信很多人都使用过LinkedHashMap,LinkedHashMap中的removeEldestEntry可以删除老旧的元素,我们可以以此来实现一个LRU缓存结构,并结合java中JUC包中的各种多线程锁机制来保证多线程安全。

以下是我遇见过的一个高级面试题:

【面试题】设计并实现一个并发安全的LRU(Least Recently Used,最近最少使用)缓存结构。要求支持以下操作:
get(key):如果键存在于缓存中,则获取其对应的值(总是返回最新添加的值),否则返回-1。
put(key, value):如果键已经存在,则更新其对应的值;如果键不存在,并且缓存未满,则添加该键值对到缓存中;如果缓存已满,则移除最近最少使用的键值对,然后再将新的键值对添加到缓存。

在这里插入图片描述

实战演示

可以采用LinkedHashMap的removeEldestEntry方法删除需要淘汰的元素,并直接使用map的get/put方法。

模拟代码

import java.util.LinkedHashMap;
import java.util.Map;/*** LRUCache* @author senfel* @date 2024/2/26 12:50*/
public class LRUCache<K, V> {private final int capacity;private LinkedHashMap<K, V> cache;public LRUCache(int capacity) {this.capacity = capacity;// 设置访问顺序和插入顺序一致,且当容量达到阈值时自动删除最近最少使用的项this.cache = new LinkedHashMap<K, V>(capacity, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}};}/*** get* @author senfel* @date 2024/2/26 12:52* @return V */public V get(K key) {synchronized (cache) {return cache.getOrDefault(key, null);}}/*** put* @author senfel* @date 2024/2/26 12:52* @return void*/public void put(K key, V value) {synchronized (cache) {cache.put(key, value);}}
}

上述代码实现了一个基本的LRU缓存结构,但get方法在key不存在时返回null而不是-1,并且没有完全解决并发访问问题。而且我们在获取到已经存在的是数据后并没有刷新数据,也就是并没有实现醉经最少使用的淘汰策略。

下面是更完善的版本,包含正确的get方法逻辑以及线程安全的put和get操作,以及在获取到数据后将数据重新刷新了。

优化后的代码

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.LinkedHashMap;
import java.util.Map;
/*** LRUCache* @author senfel* @date 2024/2/26 12:55*/
public class LRUCache<K, V> {private final int capacity;private final LinkedHashMap<K, V> cache;private final Lock lock = new ReentrantLock();public LRUCache(int capacity) {this.capacity = capacity;this.cache = new LinkedHashMap<K, V>(capacity + 1, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}};}/*** get* @author senfel* @date 2024/2/26 12:58* @return V */public V get(K key) {lock.lock();try {V value = cache.get(key);if (value != null) {// 触发访问,使得此条目变为最近使用过的cache.remove(key);cache.put(key, value);}return value;} finally {lock.unlock();}}/*** get* @author senfel* @date 2024/2/26 12:58* @return void*/public void put(K key, V value) {lock.lock();try {// 首先尝试移除旧的键值对,即使它可能不存在cache.remove(key);cache.put(key, value);} finally {lock.unlock();}}
}

写在最后

在这个高级版本的LRU缓存实现中,我们使用了LinkedHashMap作为基础数据结构,并通过重写removeEldestEntry方法实现了缓存满时自动淘汰最久未使用的元素。同时,为了保证在多线程环境下的线程安全性,我们在get和put方法上加了synchronized关键字或者使用了ReentrantLock来确保同一时间只有一个线程能执行修改缓存的操作。在get方法中,我们还额外做了一步,即在获取到key对应值后重新插入以更新其为最近使用的元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/493616.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv9来了!实时目标检测新SOTA

先上一把网上的测试效果对比: YOLOv9架构图 速度论文 代码&#xff1a;GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information 论文&#xff1a;[2402.13616] YOLOv9: Learning What You…

x-cmd pkg | g - 功能和交互更为丰富的 `ls` 替代方案

目录 简介首次用户功能特点竞品和相关作品进一步阅读 简介 g 是一项用 Go 开发的、功能和交互更为丰富的 ls 替代方案。它拥有 100 多个功能选项&#xff0c;主要是通过各式图标、各种布局选项和 git status 集成来增强视觉效果&#xff0c;并且支持多种输出格式&#xff0c;如…

在having、select子句中使用子查询

目录 在having子句中使用子查询 统计出部门平均工资高于公司平均工资的部门编号、平均工资、部门人数 在select子句中使用子查询 查询每个员工的编号、姓名、职位、部门名称 Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 在havin…

内存函数(C语言进阶)

目录 前言 1、memcpy 2、memmove 3、memcmp 4、memset 结语 前言 本篇介绍了C语言中的内存函数&#xff0c;内存函数&#xff0c;顾名思义就是处理内存的函数。 1、memcpy memcpy&#xff1a;内存拷贝函数。 相对于strcpy只能拷贝字符串来讲&#xff0c;memcpy能拷…

printf输出彩色log

在软件开发过程中&#xff0c;log调试是一个非常重要的排查问题的手段&#xff0c;log的输出结果可以帮助工程师验证其软件逻辑与执行结果是否一致。所以一个有彩色效果的log输出&#xff0c;可以帮到工程师更好的定位问题所在&#xff0c;并且利用特定的宏定义可以定位到问题出…

C#使用QQ邮箱发送邮件

简介 在c#中发送邮箱我们只需要引入官方提供的命名空间 System.Net.Mail &#xff0c;这个命名空间包含了一系列类&#xff0c;用于创建、配置和发送电子邮件消息。 这个命名空间中的一些主要类包括&#xff1a; MailMessage&#xff1a;表示一个电子邮件消息&#xff0c;包…

猫头虎分享已解决Bug || AttributeError: ‘Sequential‘ object has no attribute ‘session‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Vue 项目脚手架结构介绍

昨天&#xff0c;撰写了一篇文章&#xff0c;介绍如何 0 基础入门 Vue&#xff0c;算是讲清楚了学习 Vue 的路线图&#xff0c;今天&#xff0c;继续来介绍一些具体的东西。 一、开发工具 开发 Vue 的项目&#xff0c;我推荐使用 VS Code&#xff0c;有非常多的插件支持基于 …

【前端素材】推荐优质后台管理系统Uena平台模板(附源码)

一、需求分析 后台管理系统&#xff08;或称作管理后台、管理系统、后台管理平台&#xff09;是一种专门用于管理网站、应用程序或系统后台运营的软件系统。它通常由一系列功能模块组成&#xff0c;为管理员提供了管理、监控和控制网站或应用程序的各个方面的工具和界面。以下…

从零开始手写mmo游戏从框架到爆炸(二十三)— 装备系统一

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 目录 装备模板 装备模型 装备模板配置 加载装备模板 下一步&#xff0c;就是要考虑经验、金币、和装备掉落的问题。经验金币都好说&#xff0c;装备系统是目前需要考虑的问…

LeetCode_Java_动态规划系列(1)(题目+思路+代码)

目录 斐波那契类型 746.使用最小花费爬楼梯 矩阵 120. 三角形最小路径和 斐波那契类型 746.使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。…

Pyglet控件的批处理参数batch和分组参数group简析

先来复习一下之前写的两个例程&#xff1a; 1. 绘制网格线 import pygletwindow pyglet.window.Window(800, 600) color (255, 255, 255, 255) # 白色 lines []for y in range(0, window.height, 40):lines.append(pyglet.shapes.Line(0, y, window.width, y, colorcolo…