本地缓存与LRU

  LRU这是一种内存淘汰算法,实际上解释它也挺简单的,分为两个维度:1.容量;2.时间。

  我们有一个有界队列,如果不断地往其中写入数据,总会满的,这个时候需要对数据进行淘汰。如果超过了容量,在插入数据的时候应该淘汰哪些数据。那就是不经常用的,冷数据。LRU的主旨是保存最近经常使用的数据,经常使用的数据叫做热点数据,保留它们的价值在于:它们经常被访问,从缓存中取就不用去频繁访问数据库等其他存储了,这样就提高了程序效率。

  一般的缓存容器都是双端队列型的,有head和tail。容量达到上限插入的时候,tail的元素会出队。如果队中元素经常被访问,那它就会被提到head端去。JDK提供了一种根据容量来出队的数据结构:LinkedHashMap。

  它的结构是HashMap的结构,在这基础上,它的每个链表对象Entity,多加入了pre和next指针(大概意思),用来表示插入顺序的。所以它能保证访问时候走的是Hash的体系O(1),而在遍历的时候可以识别插入顺序;与此同时,它还可以实现LRU。  

public class LRULinkedMap<K,V> extends LinkedHashMap<K,V>{
    private static final int DEFAULT_CAPACITY = 1<<4;
    private static final float DEFAULT_FACTOR = 0.75f;
    private static final int DEFAULT_LIMIT = DEFAULT_CAPACITY/2;

    private int limit;

    public LRULinkedMap(){
        super(DEFAULT_CAPACITY,DEFAULT_FACTOR,true);
        limit = DEFAULT_LIMIT;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {

        if(this.size() > limit){
            System.out.println("移除最老的节点:"+eldest.getKey()+","+eldest.getValue());
            return true;
        }else {
            return false;
        }
    }
}

  重写它的removeEldestEntity方法告诉它什么时候将最老的节点移除掉就好。这里有两个不好的地方,一是它线程不安全;二是它只能根据容量移除,如果要根据时间的话你得在放对象的时候对象上带有时间戳,自己实现。

        LRULinkedMap<Integer,String> lruLinkedMap = new LRULinkedMap();
        for(int i=0;i<20;i++){
            String val = "序号:"+i;
            System.out.println("添加节点:"+i+","+val);
            lruLinkedMap.put(i,val);
        }
添加节点:0,序号:0
添加节点:1,序号:1
添加节点:2,序号:2
添加节点:3,序号:3
添加节点:4,序号:4
添加节点:5,序号:5
添加节点:6,序号:6
添加节点:7,序号:7
添加节点:8,序号:8
移除最老的节点:0,序号:0
添加节点:9,序号:9
移除最老的节点:1,序号:1
添加节点:10,序号:10
移除最老的节点:2,序号:2
添加节点:11,序号:11
移除最老的节点:3,序号:3
添加节点:12,序号:12
移除最老的节点:4,序号:4
添加节点:13,序号:13
移除最老的节点:5,序号:5
添加节点:14,序号:14
移除最老的节点:6,序号:6
添加节点:15,序号:15
移除最老的节点:7,序号:7
添加节点:16,序号:16
移除最老的节点:8,序号:8
添加节点:17,序号:17
移除最老的节点:9,序号:9
添加节点:18,序号:18
移除最老的节点:10,序号:10
添加节点:19,序号:19
移除最老的节点:11,序号:11

 

  google提供了我们一种JVM缓存的工具

        <!--jvm本地缓存-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>24.0-jre</version>
        </dependency>
        Cache<Integer,String> cache = CacheBuilder.newBuilder()
                .maximumSize(1<<4)
                .expireAfterWrite(3,TimeUnit.SECONDS)
                .build();

        cache.put(1,"序号1");
        System.out.println("立即获取:"+cache.getIfPresent(1));
        Thread.sleep(5000);
        System.out.println("超时获取:"+cache.getIfPresent(1));
立即获取:序号1
超时获取:null

  一个简单的构建就能使用JVM缓存,本地缓存的优势在于:节省了网络调用(Redis,Memcached)、数据库访问、RPC调用。  

  它的put方法是这样的,很熟悉,就是1.7的hashmap分段锁,所以它又是线程安全的。

    public V put(K key, V value) {
        Preconditions.checkNotNull(key);
        Preconditions.checkNotNull(value);
        int hash = this.hash(key);
        return this.segmentFor(hash).put(key, hash, value, false);
    }

  此外它还提供了LoadingCache,什么概念呢,失效的Key它会调用你重写的方法重新载入,也就是带刷新数据的功能。

  缓存的使用主旨,应该在动静分离上,对于一些不怎么变化的数据,或者有时效性又经常访问的数据,使用缓存防止磁盘IO,是一个提升系统性能的方案。

转载于:https://www.cnblogs.com/chentingk/p/11218664.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值