您现在的位置是:亿华云 > 知识
面试官:如何设计和实现一个带过期时间的本地缓存?
亿华云2025-10-06 00:12:16【知识】6人已围观
简介来源:JAVA日知录在日常开发中有很多这样的场景:有一些业务系统的配置信息,数据量不大,修改频率不高,但是访问很频繁。如果每次程序都从数据库或集中式缓存中获取,受限于硬盘 I/O性能、远程网络访问限制
来源:JAVA日知录
在日常开发中有很多这样的面试场景:有一些业务系统的配置信息,数据量不大,官何修改频率不高,设计但是和实缓存访问很频繁。如果每次程序都从数据库或集中式缓存中获取,本地受限于硬盘 I/O性能、面试远程网络访问限制等,官何程序的设计执行效率不高。在这样的和实缓存业务场景中,我们可以通过本地缓存来提升数据访问的本地效率。
今天我们来基于ConcurrentHashMap与ScheduledThreadPoolExecutor来实现一个线程安全的面试本地缓存:LocalCache。在LocalCache中支持永久缓存与临时缓存,官何永久缓存的设计数据一直有效,临时缓存的和实缓存数据在指定时间到期之后会自动从缓存中移出。
LocalCache提供了数据安全的本地增、删、改、查功能,具体方法如下所示:
方法名称方法说明put(String key , V value)向缓存中插入数据,数据永久有效put(String key , V value , int seconds)向缓存中插入数据,数据根据设定的时间生效,时间到期会从缓存中移出containKey(String key)判断缓存中是否包含对应的云服务器keyget(String key)根据key从缓存中获取数据remove(String key)移出缓存中对应key的数据shutdownNow()关闭缓存池1. 设计原理
LocalCache主要由3个部分组成:数据缓存、数据超时时间、数据清理任务。数据缓存和数据超时时间都采用ConcurrentHashMap来存储数据,数据超时时间中Key为数据存储的键,value是数据的时间戳。数据清理任务采用ScheduledThreadPoolExecutor实现任务调度,默认的任务线程数为1,这样可以避免多线程带来的并发修改问题,同时线程都是内存操作,这样单线程同样具备高性能。
本地缓存的设计如下图所示:

每次项缓存中插入数据时,LocalCache首先会将数据插入到ConcurrentHashMap中。然后判断有没有设置超时时间,如果有超时时间,LocalCache会将失效时间插入到ConcurrentHashMap中,并创建数据清理任务,之后任务提交到ScheduledThreadPoolExecutor线程池中。
每次从缓存中查询数据,LocalCache会直接从ConcurrentHashMap中读取数据。
定时任务线程池会按照超时时间来触发数据清理任务,数据清理任务会从数据时长的缓存池中获取Key对应的时间,判断当前Key对应的云服务器提供商数据是否已经到期了。如果数据已经到期了,LocalCache会调用remove方法将数据从缓存池中移除。
2. 实现方案
LocalCache作为本地缓存的接口,定义了数据插入、数据删除、数据查询的相关接口方法。DefaultLocalCache 定义了两个ConcurrentHashMap变量:dataMap和timeOutMap。dataMap用来缓存数据信息,timeOutMap用来存储数据失效的时间戳,同时还定义了数据清理任务ClearTask,ClearTask负责将过期的数据从dataMap中移除。UML图如下所示:

3. 代码展示
3.1 接口定义public interface LocalCache<V>{
/
*** 插入数据,数据永久有效
*/ boolean put(String key, V value);
/
*** 插入数据,在指定时间内生效
*/ boolean put(String key, V value, int seconds);
/
*** 是否包含指定的key
*/ boolean containKey(String key);
/
*** 获取指定Key的值
*/ V get(String key);
/
*** 从缓存中移除key对应的数据
*/ void remove(String key);
void shutdownNow();
}
在接口LocalCache中定义了两个数据插入的put接口:一个没有到期时间,另一个有到期时间。没有到期时间表示数据永久有效,有到期时间的数据会在到期后从缓存中移除。
接口实现在接口实现DefaultLocalCache内部定义了三个常量:缓存的默认大小DEFAULT_CAPACITY、最大容量MAX_CAPACITY、定时线程池的香港云服务器大小DEFAULT_THREAD_SIZE。核心代码如下:
public class DefaultLocalCache<V> implements LocalCache<V>{
// 默认容量 private static final int DEFAULT_CAPACITY = 1024;
private static final int MAX_CAPACITY = 100000;
private static final int DEFAULT_THREAD_SIZE = 1;
private final intmaxSize;
//数据map private volatile ConcurrentHashMap
ConcurrentHashMap
ScheduledExecutorService executorService;
public DefaultLocalCache(){
maxSize = MAX_CAPACITY;
dataMap = newConcurrentHashMap<>(DEFAULT_CAPACITY);
timeOutMap = newConcurrentHashMap<>(DEFAULT_CAPACITY);
executorService = newScheduledThreadPoolExecutor(DEFAULT_THREAD_SIZE) ;
}
public DefaultLocalCache(int size){
maxSize = size;
dataMap = newConcurrentHashMap<>(DEFAULT_CAPACITY);
timeOutMap = newConcurrentHashMap<>(DEFAULT_CAPACITY);
executorService = newScheduledThreadPoolExecutor(DEFAULT_THREAD_SIZE) ;
}
@Override public boolean put(String key, V value){
//检查容量 if(checkCapacity()){
dataMap.put(key,value);
return true;
}
return false;
}
@Override public boolean put(String key, V value, int seconds){
if(checkCapacity()){
dataMap.put(key,value);
if(seconds >= 0){
timeOutMap.put(key,getTimeOut(seconds));
ClearTask task = newClearTask(key);
executorService.schedule(task, seconds, TimeUnit.SECONDS);
}
}
return false;
}
......
class ClearTask implements Runnable{
privateString key;
public ClearTask(String key){
this.key = key;
}
@Override public void run(){
//判断缓存中是否有key if(timeOutMap.contains(key)){
//获取失效时间Long expire = timeOutMap.get(key);
//如果失效时间大于0,并且比当前时间小,则删除缓存 if(expire > 0){
longnow = System.currentTimeMillis();
if(now >= expire){
remove(key);
}
}
}
}
}
}
在LocalCache的默认实现DefaultLocalCache中,基于ConcurrentHashMap与ScheduledThreadPoolExecutor结合使用,使得LocalCache支持永久缓存与临时缓存两种能力。
很赞哦!(35)
相关文章
- 如果你的潜在终端必须是这个米(域名),那么潜在终端并不多,也没有硬通货,那么你的域名应该在终端有兴趣购买时出售。否则,你可能得自己留着吃。
- 每秒100W次的计数,架构原来可以这样设计!
- 微服务之服务挂的太干脆,Nacos还没反应过来,怎么办?
- 如何调试您的Python代码?
- 公司和个人选域名方法一样吗?有什么不同?
- Gartner APM 魔力象限技术解读——全量存储?No! 按需存储?YES
- 100W并发秒杀系统架构
- @wraps 修饰器:让你的 Python 代码更加简短可爱 | 从简单实例来认识它
- 第六:这个圈子里的域名确实是赚钱的一些大玩家,至于小米农,有多少赚钱?几乎没有,也就是说,轿子里只有一个人,而且大多数人都抬着轿子。
- 有了这个 4.5 万 Star 的工具,可在浏览器中运行最强编辑器 VS Code!
站长推荐
众所周知,com域名拥有最大的流通市场和流通历史。最好选择com域名,特别是在购买域名时处理域名。其次可以是cn域名、net域名、org域名等主流域名,现在比较流行的王域名和顶级域名,都是值得注册和投资的。
一文带你了解MindSpore支持的万亿级参数超大模型关键技术!
解读官方博客:React18真的来了
代码写的烂,经常被同事怼,教你一招!
6、提示添加成功,点击确认进行最后的确定操作。一般10分钟就解析生效,可以用域名进行访问了。
一篇文章带你弄懂Python基础之列表相关操作和嵌套
Linkerd 2.10(Step by Step)—混沌工程之注入故障
5个用于测试Vue.js应用程序的有用工具和库