JAVA延迟队列(实现数据的缓存和定时清理)

在延迟队列中所保存的每一个元素内容.每当时间一到,(compareTo进行比较,getDelay()获取延迟时间),都会自动进行队里数据的弹出操作;
使用延迟队列(模拟讨论会依次离开的场景)

public class Student implements Delayed {
    //姓名
    private String name;
    //离开时间
    private long expire;
    //停留时间
    private long delay;

    public Student(String name,long delay,TimeUnit unit){
        this.name = name;
        //转换时间单位为毫秒
        this.delay = TimeUnit.MILLISECONDS.convert(delay,unit);
        //失效时间计算
        this.expire = System.currentTimeMillis() + this.delay;
    }

    public String toString() {
        return this.name + "同学已经达到预计的停留时间"+TimeUnit.SECONDS.convert(this.delay,TimeUnit.MILLISECONDS)+"秒,已经离开了";
    }
    /**
     * 队列弹出计算
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        int i = (int) (this.delay - this.getDelay(TimeUnit.MILLISECONDS));
        return i;
    }
    /**
     *延迟时间计算
     * @param
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        long convert = unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        return convert;
    }
}
public class JUCDemo {
    public static void main(String[] args) throws InterruptedException {
        //定义延迟队列
        BlockingQueue<Student> queue = new DelayQueue<>();
        //保存队列信息
        queue.put(new Student("李",3, TimeUnit.SECONDS));
        queue.put(new Student("王",5,TimeUnit.SECONDS));

        //判断队列是否有数据
        while (!queue.isEmpty()){
            //获取弹出数据
            Student take = queue.take();
            System.out.println(take);
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

输出结果:

李同学已经达到预计的停留时间3,已经离开了
王同学已经达到预计的停留时间5,已经离开了

使用延迟队列的主要原因是它可以实现队列的定时清理操作.那么基于这样的自动清理机制就可以实现数据缓存的操作控制.这样的操作可以极大的提升项目的并发性能;
(在时间开发中,如果是基于数据库的查询操作,那么在多线并发量较高的情况下就有可能产生严重的性能问题,例如一个热门新闻可能会有上千万的访问量,这个时候采用直接数据库的读取模式就非常不理智.为了解决这一的问题,可以采用缓存模式,将一些重要的数据直接放到缓存里面.当不同的线程查询相同数据时先判断缓存中是否有指定内容,如果存在,则进行直接读取,如果不存在,再进行数据库加载.对于缓存中的内容还要考虑无效数据的清理问题,而有了延迟队列这种自动弹出的机制存在.这一操作实现就会变得非常容易)

下面实现一个新闻数据的缓存操作,考虑到可能会保存有多个数据,所以讲通过Map集合实现存储,同时考虑到缓存数据的修改安全性问题,使用ConcurrentHashMap子类,另外对于数据的弹出操作将通过守护线程进行处理.

public class News {
    private long nid;
    private String title;
    public News(long nid, String title){
        this.nid = nid;
        this.title = title;
    }
    public String toString(){
        return "新闻数据---->编号:"+this.nid+",标题:"+title;
    }
}
public class Cache<K,V> {
    //时间单位
    private static final TimeUnit TIME = TimeUnit.SECONDS;
    //缓存时间
    private static final long DELAY_SECONDS = 2;
    //缓存集合
    private Map<K,V> cacheObjects = new ConcurrentHashMap<>();
    private BlockingQueue<DelayedItem<Pair>> queue = new DelayQueue<>();

    //启动线程
    public Cache(){
        Thread thread = new Thread(()->{
            while (true){
                try{
                    //数据弹出消费
                    DelayedItem<Pair> item = Cache.this.queue.take();
                    if (item != null){
                        //获取内容
                        Pair pair = item.getItem();
                        //删除数据
                        Cache.this.cacheObjects.remove(pair.key,pair.value);
                    }
                }catch (Exception e){}
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    /**
     * 保存数据
     * @param key
     * @param value
     */
    public void put(K key,V value) throws InterruptedException {
        V oldValue= this.cacheObjects.put(key,value);
        //删除已有数据
        if (oldValue != null){
            this.queue.remove(oldValue);
        }
        //重新保存
        this.queue.put(new DelayedItem<Pair>(new Pair(key,value),DELAY_SECONDS,TIME));
    }

    /**
     * 获取缓存数据
     * @param key
     * @return
     */
    public V get(K key){
        return this.cacheObjects.get(key);
    }

    /**
     * 封装保存数据
     */
    private class Pair{
        private K key;
        private V value;

        public Pair(K key,V value){
            this.key = key;
            this.value = value;
        }
    }

    /**
     * 延迟数据保存
     * @param 
     */
    private class DelayedItem<T> implements Delayed{
        private T item;
        private long delay;
        private long expire;

        public DelayedItem(T item,long delay,TimeUnit unit){
            this.item = item;
            this.delay = TimeUnit.MILLISECONDS.convert(delay,unit);
            this.expire = System.currentTimeMillis() + this.delay;
        }

        public T getItem(){
            return this.item;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.expire - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return (int)(this.delay - this.getDelay(TimeUnit.MILLISECONDS));
        }
    }

}
public class JUCDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Cache<Long,News> cache = new Cache<>();
        cache.put(1L,new News(1L,"程序员为什么秃"));
        cache.put(2L,new News(2L,"程序员为什么宅"));
        cache.put(3L,new News(3L,"程序员为什么扣"));

        System.out.println(cache.get(1L));
        System.out.println(cache.get(2L));
        //模拟延迟时间.2秒后清除缓存
        TimeUnit.SECONDS.sleep(3);
        System.out.println(cache.get(3L));
    }
}

输出结果:

新闻数据---->编号:1,标题:程序员为什么秃
新闻数据---->编号:2,标题:程序员为什么宅
null

在程序中考虑到缓存数据的自动清理问题,所以使用了延迟队列保存所以的数据信息(同时还有一份数据信息保存在Map集合中),为了保证延迟队列中的数据弹出后可以进行Map集合相应数据的删除.所以定义了一个守护线程接收延迟队列弹出的内容.由于本程序设置了默认的缓存时间为2秒,这样当2秒一过数据就会被删除.

你可能感兴趣的:(队列,java,缓存)