基于LRULinkedHashMap的缓存的实现

LRULinkedHashMap实现缓存


前言

LRU是Least Recently Used ,最近最少使用算法,第一次见到这个名词是在学操作系统、后面学网络原理时也遇到过一次。这一次在代码里看到大佬了用了这个实现缓存,研究了一下~ 一开始还挺纳闷,为啥不直接用先用现有的工具如redis去作缓存处理,大佬给出的解释是,用这个实现缓存是在JVM中实现的,效率会比redis快很多,特别数据量多的时候(他那边是有这种情况),根据压测的效果来看,当数据量达到10w时,可以比redis快100倍左右。不过,话又说回来了,这种缓存也有相应的弊端,持久化是个问题?JVM也需要作相应的调优,缓存算法也需要限制capacity,不然JVM压力过大,可能会造成内存溢出。

原理

该算法基于LinkedHashMap,链表加上一个哈希,链表对元素增删的方便性,加上哈希读取元素的快速性,使得整个操作的时间复杂度大大降低。

其中,链表是一个双向链表,LinkedHashMap 内部维护一个双向队列,在初始化时也会给定一个缓存大小的阈值capacity。初始化时自定义是否需要删除最近不常使用的数据,如果是则会按照我们重写的方式进行实现(就是重写LinkedHashMap中的removeEldestEntry方法)。

LinkedHashMap可以根据我们放入map的元素的顺序进行打印,当我们访问其中某个元素时,它会将访问过的元素放置于底端,而头部则是比较少访问的元素了~当元素超过规定的容量时,LinkedHashMap会优先删除头部元素,则为LRU的实现提供了捷径。

实现

通过重入锁实现多线程的版本,确保数据的安全性~

public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {

    private final int maxCapacity;

    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    private final Lock lock = new ReentrantLock();

    public LRULinkedHashMap(int maxCapacity) {
        super(maxCapacity, DEFAULT_LOAD_FACTOR, true);
        this.maxCapacity = maxCapacity;
    }
    
    /**
     * 实现LRU的关键方法,如果map里面的元素个数大于了缓存最大容量,则删除链表的顶端元素
     * 初始化时自定义是否需要删除最近不常使用的数据,如果是则会按照实现的方式管理数据。
     * @param eldest
     * @return
     */
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxCapacity;
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            lock.lock();
            return super.containsKey(key);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public V get(Object key) {
        try {
            lock.lock();
            return super.get(key);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public V put(K key, V value) {
        try {
            lock.lock();
            return super.put(key, value);
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        try {
            lock.lock();
            return super.size();
        } finally {
            lock.unlock();
        }
    }

    public void clear() {
        try {
            lock.lock();
            super.clear();
        } finally {
            lock.unlock();
        }
    }

    public Collection<Map.Entry<K, V>> getAll() {
        try {
            lock.lock();
            return new ArrayList<>(super.entrySet());
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        LRULinkedHashMap<Integer,Integer> test = new LRULinkedHashMap<>(4);
        test.put(0,0);
        test.put(1,1);
        test.put(2,2);
        test.put(3,3);
        //访问到的元素移动到了底部
        System.out.println("访问第一个元素:="+test.get(0));
        test.put(4,4);
        Iterator it = test.entrySet().iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个使用Spring Boot和Redis实现缓存的样例: 1. 添加Redis依赖 在pom.xml文件中添加如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置Redis 在application.properties或application.yml文件中添加如下配置: ```yaml spring: redis: host: localhost port: 6379 ``` 3. 编写缓存代码 在需要缓存的方法上添加@Cacheable注解,并指定缓存的名称和键值,如下所示: ```java @Service public class UserService { @Autowired private UserDao userDao; @Cacheable(value = "userCache", key = "#id") public User getUserById(Long id) { return userDao.getUserById(id); } } ``` 上述代码中,@Cacheable注解指定了缓存的名称为"userCache",并且使用方法的参数作为缓存的键值。 4. 清理缓存 在需要清理缓存的方法上添加@CacheEvict注解,如下所示: ```java @Service public class UserService { @Autowired private UserDao userDao; @CacheEvict(value = "userCache", key = "#id") public void deleteUserById(Long id) { userDao.deleteUserById(id); } } ``` 上述代码中,@CacheEvict注解指定了需要清理的缓存名称和键值。当调用deleteUserById方法时,缓存中与该键值相关的数据将被清除。 需要注意的是,这只是一个简单的缓存实现样例,实际应用中需要根据具体的业务需求和系统特点来选择合适的缓存框架和配置方式。同时也需要注意缓存的一致性和安全性问题,避免出现数据不一致或者泄露敏感信息的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

legendaryhaha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值