文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

面试官:如何设计和实现一个带过期时间的本地缓存?

2024-11-30 00:16

关注

今天我们来基于ConcurrentHashMap与ScheduledThreadPoolExecutor来实现一个线程安全的本地缓存:LocalCache。在LocalCache中支持永久缓存与临时缓存,永久缓存的数据一直有效,临时缓存的数据在指定时间到期之后会自动从缓存中移出。

LocalCache提供了数据安全的增、删、改、查功能,具体方法如下所示:

方法名称

方法说明

put(String key , V value)

向缓存中插入数据,数据永久有效

put(String key , V value , int seconds)

向缓存中插入数据,数据根据设定的时间生效,时间到期会从缓存中移出

containKey(String key)

判断缓存中是否包含对应的key

get(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 {
    
    boolean put(String key, V value);

    
    boolean put(String key, V value, int seconds);

    
    boolean containKey(String key);

    
    V get(String key);

    
    void remove(String key);

    void shutdownNow();
}

在接口LocalCache中定义了两个数据插入的put接口:一个没有到期时间,另一个有到期时间。没有到期时间表示数据永久有效,有到期时间的数据会在到期后从缓存中移除。

接口实现

在接口实现DefaultLocalCache内部定义了三个常量:缓存的默认大小DEFAULT_CAPACITY、最大容量MAX_CAPACITY、定时线程池的大小DEFAULT_THREAD_SIZE。核心代码如下:

public class DefaultLocalCache implements LocalCache {
    // 默认容量
    private static final int DEFAULT_CAPACITY = 1024;
    private static final int MAX_CAPACITY = 100000;

    private static final int DEFAULT_THREAD_SIZE = 1;

    private final int maxSize;

    //数据map
    private volatile ConcurrentHashMap dataMap;
    //过期时间
    private final ConcurrentHashMap timeOutMap;

    //定时任务
    private final ScheduledExecutorService executorService;

    public DefaultLocalCache() {
        maxSize = MAX_CAPACITY;
        dataMap = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
        timeOutMap = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
        executorService = new ScheduledThreadPoolExecutor(DEFAULT_THREAD_SIZE) ;
    }

    public DefaultLocalCache(int size) {
        maxSize = size;
        dataMap = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
        timeOutMap = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
        executorService = new ScheduledThreadPoolExecutor(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 = new ClearTask(key);
                executorService.schedule(task, seconds, TimeUnit.SECONDS);
            }
        }
        return false;
    }

 ......

    class ClearTask implements Runnable{
        private String 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){
                    long now = System.currentTimeMillis();
                    if(now >= expire){
                        remove(key);
                    }
                }
            }
        }
    }
}

在LocalCache的默认实现DefaultLocalCache中,基于ConcurrentHashMap与ScheduledThreadPoolExecutor结合使用,使得LocalCache支持永久缓存与临时缓存两种能力。

来源:JAVA日知录内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯