Java中的LRU算法

先回顾一下redis中的LRU

Redis中的数据量通常很庞大,如果每次对全量数据进行排序,势必将对服务吞吐量造成影响。因此,Redis在LRU淘汰部分key时,使用的是采样并计算近似LRU的,因此淘汰的是局部LRU数据。
Redis内存淘汰策略
maxmemory-policy配置可选参数:

  • noeviction:不淘汰,内存超限后写命令会返回错误(如OOM, del命令除外)
  • allkeys-lru:所有key的LRU机制 在所有key中按照最近最少使用LRU原则剔除key,释放空间
  • volatile-lru:易失key的LRU 仅以设置过期时间key范围内的LRU(如均为设置过期时间,则不会淘汰)
  • allkeys-random:所有key随机淘汰 一视同仁,随机
  • volatile-random:易失Key的随机 仅设置过期时间key范围内的随机
  • volatile-ttl:易失key的TTL淘汰 按最小TTL的key优先淘汰

allkeys-lru基本上使用的最多,其他的很少使用。

redis中并未使用真正的LRU算法,真正的LRU会耗费大量内存,Redis最主要的做法:淘汰时,随机取多个keys,找到最老的进行淘汰.虽不能保证一定淘汰最老的,但倾向于淘汰偏老的keys。

  此外官方文档上说,如果数据访问模模型越符合幂次定律,则redis 的LRU近似算法和真实的LRU算法越接近(看的不是太明白,可以翻翻相关资料,大概就是说,20%数据访问非常频繁,80%的数据访问不是很频繁时,redis的LRU近似算法和真实的LRU算法是相差最小的)。

Java中的LRU实现

1.用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。

2.利用一个链表来实现,每次新插入数据的时候将新数据插到链表的头部;每次缓存命中(即数据被访问),则将数据移到链表头部;那么当链表满的时候,就将链表尾部的数据丢弃。

对于第一种方法,需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。对于第二种方法,链表在定位数据的时候时间复杂度为O(n)。所以这两种方法都不好!接下来会通过使用链表+map的结构来实现LRU算法

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * description
 * 
 * @author fanglingxiao
 * @date 2019/9/18
 */
public class LRUCache<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 LRUCache(int maxCapacity) {
        super(maxCapacity, DEFAULT_LOAD_FACTOR, true);
        this.maxCapacity = maxCapacity;
    }

    @Override
    protected boolean removeEldestEntry(java.util.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();
        }
    }

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

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

    public Collection<Map.Entry<K, V>> getAll() {
        try {
            lock.lock();
            return new ArrayList<Map.Entry<K, V>>(super.entrySet());
        } finally {
            lock.unlock();
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值