HashMap+线程池+优先队列实现带过期时间的LRUCache

LRU算法

Least Recently Used 意思是最近最少使用,是一种常用的置换算法,当你向一个队列或者其他容器中添加任务或者页面时容器已满,该算法会自动清理一个位置给你,今天我实现的就是添加了过期时间同时能够自动清理过期时间的LRU算法

下面是实现和思路

package interview;


import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @ Author     :fqg
 * @ Date       :Created in 12:12 2020/8/25
 */
public class TimeOutLRU {
    static class Node implements Comparable<Node>{
        String key;
        Object value;
        Long expireTime;
        public Node(){}
        public Node(String key, Object value, long expireTime){
            this.key = key;
            this.value = value;
            this.expireTime = expireTime;
        }
        //内比较器的重写
        @Override
        public int compareTo(Node o) {
            long l = this.expireTime - o.expireTime;
            if(l < 0) return -1;
            if(l > 0) return 1;
            return 0;
        }
    }
    //线程池中的worker线程
    static class ExpireThread implements Runnable{

        @Override
        public void run() {
            long now = System.currentTimeMillis();
            while (true) {
                Node n = queue.peek();
                if(n == null || n.expireTime > now) return;
                queue.remove(n);
                map.remove(n.key);
                System.out.println("清除成功");
            }
        }
    }
    //内部容量,保证线程安全使用concurrentHashMap同时将queue设置成单例模式
    private static ConcurrentHashMap<String, Node> map;
    //使用优先队列,在队列内将Node进行排序
    private static volatile PriorityQueue<Node> queue;
    //线程池,这里使用定时任务线程池
    private static ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(10);
    //双从检查锁
    private PriorityQueue<Node> getInstance(){
        if(queue == null){
            synchronized(TimeOutLRU.class){
                if(queue == null){
                    queue = new PriorityQueue<>(1024);
                }
            }
        }
        return queue;
    }
    //constructor
    public TimeOutLRU(){
        map = new ConcurrentHashMap<>();
        queue = getInstance();
        //每隔三秒获取queue的头结点,查看是否过期。
        pool.scheduleWithFixedDelay(new ExpireThread(), 0, 3, TimeUnit.SECONDS);
    }
    //expose method
    public Object set(String key, Object value, long time){
        //获取过期时间
        long timeout = System.currentTimeMillis() + time;
        Node newNode = new Node(key, value, timeout);
        Node old = map.put(key, newNode);
        queue.offer(newNode);
        if(old != null){
            queue.remove(old);
            return old.value;
        }
        return null;
    }

    public Object get(String key){
        Node n = map.get(key);
        return n == null ? null : n.value;
    }

    public static void main(String[] args) throws InterruptedException {
        TimeOutLRU timeOutLRU = new TimeOutLRU();
        timeOutLRU.set("1","fqg",1000);
        Thread.sleep(1000);
        Object str = timeOutLRU.get("1");
        System.out.println(str);
    }
}

 测试结果如下

问题:

1.实现思路总结

重新定义个一个Node添加过期时间字段,使用优先队列和ConcurrentHashaMap来存值和过期检测,使用定时任务线程池来循环检测优先队列的头结点是否过期,如果过期就删除(实现过程中没有实现队列满了,或者其他特殊情况,当时我想的是如果队列满了,直接删除头结点就行,因为毕竟是个优先级队列,头结点的过期时间永远是最近的)

2.Comparator和Comparable两个比较器的区别

一个在内部使用一个在外部使用,用处不同意义相同但是体现的思想不同,内比较器体现的封装,外比较器体现的扩展。

原文地址:https://www.cnblogs.com/frank9571/p/13559544.html