java实现带过期时间的缓存

private static ScheduledExecutorService swapExpiredPool
            = new ScheduledThreadPoolExecutor(10);

    private ReentrantLock lock = new ReentrantLock();

    private ConcurrentHashMap<String, Node> cache = new ConcurrentHashMap<>(1024);
    /**
     * 让过期时间最小的数据排在队列前,在清除过期数据时
     * ,只需查看缓存最近的过期数据,而不用扫描全部缓存
     *
     * @see Node#compareTo(Node)
     * @see SwapExpiredNodeWork#run()
     */
    private PriorityQueue<Node> expireQueue = new PriorityQueue<>(1024);

    public LocalCache() {

        //使用默认的线程池,每5秒清除一次过期数据
        //线程池和调用频率 最好是交给调用者去设置。
        swapExpiredPool.scheduleWithFixedDelay(
                new SwapExpiredNodeWork(), 5, 5, TimeUnit.SECONDS);
    }

    public Object set(String key, Object value, long ttl) {

        Assert.isTrue(StringUtils.hasLength(key), "key can‘t be empty");
        Assert.isTrue(ttl > 0, "ttl must greater than 0");

        long expireTime = System.currentTimeMillis() + ttl;
        Node newNode = new Node(key, value, expireTime);
        lock.lock();
        try {
            Node old = cache.put(key, newNode);
            expireQueue.add(newNode);
            //如果该key存在数据,还要从过期时间队列删除
            if (old != null) {
                expireQueue.remove(old);
                return old.value;
            }
            return null;
        } finally {
            lock.unlock();
        }

    }

    /**
     * 拿到的数据可能是已经过期的数据,可以再次判断一下
     * if(n.expireTime<System.currentTimeMillis()){
     * return null;
     * }
     * 也可以直接返回整个节点Node ,交给调用者去取舍
     * <p>
     * <p>
     * 无法判断不存在该key,还是该key存的是一个null值,如果需要区分这两种情况
     * 可以定义一个全局标识,标识key不存在
     * public static final NOT_EXIST = new Object();
     * 返回值时
     * return n==null?NOT_EXIST:n.value;
     */
    public Object get(String key) {
        Node n = cache.get(key);
        return n == null ? null : n.value;
    }

    /**
     * 删出KEY,并返回该key对应的数据
     */
    public Object remove(String key) {
        lock.lock();
        try {
            Node n = cache.remove(key);
            if (n == null) {
                return null;
            } else {
                expireQueue.remove(n);
                return n.value;
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 删除已经过期的数据
     */
    private class SwapExpiredNodeWork implements Runnable {

        @Override
        public void run() {

            long now = System.currentTimeMillis();
            while (true) {
                lock.lock();
                try {
                    Node node = expireQueue.peek();
                    //没有数据了,或者数据都是没有过期的了
                    if (node == null || node.expireTime > now) {
                        return;
                    }
                    cache.remove(node.key);
                    expireQueue.poll();

                } finally {
                    lock.unlock();
                }
            }
        }
    }

    private static class Node implements Comparable<Node> {
        private String key;
        private Object value;
        private long expireTime;

        public Node(String key, Object value, long expireTime) {
            this.value = value;
            this.key = key;
            this.expireTime = expireTime;
        }

        /**
         * @see SwapExpiredNodeWork
         */
        @Override
        public int compareTo(Node o) {
            long r = this.expireTime - o.expireTime;
            if (r > 0) {
                return 1;
            }
            if (r < 0) {
                return -1;
            }
            return 0;
        }

    }

  

原文地址:https://www.cnblogs.com/matd/p/12074173.html

时间: 2024-10-12 19:29:31

java实现带过期时间的缓存的相关文章

IIS使用十大原则,(IIS过期时间,IIS缓存设置) 【转载】

1. 自定义错误页虽然自定义错误页很简单,但只有少数管理员有效地利用了它.管理员可以在MMC中将HTTP错误信息映像到服务器上的绝对URL或是某个文件,更为详细的信息可以在这里找到.如果你嫌这太麻烦,想要更简单的方法,或者你希望开发者自己定义错误页,同时又不想让他们具有使用MMC的权限,你可以使用类似 Customer Error 这样的工具. 2. MetaBase研究如果你认为Apache功能强大是因为它有一个配置文件,那么你应该看一看IIS的MetaBase有多棒.使用MetaBase,管

java操作Redis缓存设置过期时间

关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息转为json串作为value保存在redis中,通常做法是: //生成token String token = UUID.randomUUID().toString(); //把用户信息写入redis jedisClient.set(REDIS_USER_SESSION_KEY + ":"

本地缓存过期时间与JVM垃圾回收

背景:在mo的业务中,调整更长的本地缓存的有效时间,可以一定程度减少主动回源的次数,并减少YGC的频率,但是也可能会有一些新问题. 首先要知道: 1.JVM中的堆内存是一个可以被一个进程内的所有线程共享的,而本地缓存一般就放在这块堆内存上. 2.堆内存一般分为新生代.老生代和永久代,永久带是方法区,不在本次讨论范围内. 3.YGC发生在新生代(创建新对象,空间不够时触发),FGC发生在老生代(老生代满了才触发). 4.无论YGC还是FGC,都只清除JVM认为是垃圾的对象. 5.新生代又分为ede

java parse 带英文单词的日期字符串 转 date (转化新浪微博api返回的时间)

拂晓风起 专注前端技术cocos2d.js.flash.html5,联系:[email protected].如果读者要找腾讯工作机会,请不吝推荐简历. 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 227  文章- 0  评论- 336 java parse 带英文单词的日期字符串 转 date (转化新浪微博api返回的时间) 大家一般很少格式化或者parse带有Sun Nov等英文单词的字符串. 如果格式化英文月份的字符串,记得带上Locale.US参数,否则,JRE会按照当前地区

如果将缓存“滑动过期时间”设置为1秒会怎样?

今天编写了一个采用ASP.NET Caching的组件,在为它编写Unit Test的过程中发现了一个有趣的问题,接下来我通过一个简单的实例说明这个问题.我们在一个控制台应用中编写了如下一段程序,这个段程序很简单:我们通过HttpRuntime的静态属性Cache得到表示当前缓存的Cache对象,并调用其Insert方法对当前的时间实施缓存.需要注意的是,我们采用“滑动时间”过期策略,并将这个滑动时间设置为1秒.

java设置配置session过期时间的方法

1) Timeout in the deployment descriptor (web.xml)以分钟为单位 代码如下 复制代码 <web-app ...> <session-config> <session-timeout>20</session-timeout> </session-config></web-app> 上面这种设置,对整个web应用生效.当客户端20分钟内都没有发起请求时,容器会将session干掉. 2) Ti

redis 一二事 - 设置过期时间,以文件夹形式展示key显示缓存数据

在使用redis时,有时回存在大量数据的时候,而且分类相同,ID相同 可以使用hset来设置,这样有一个大类和一个小分类和一个value组成 但是hset不能设置过期时间 过期时间只能在set上设置 1 // 向redis中添加缓存 2 jedisClient.set(REDIS_ITEM_KEY + ":" + itemId + ":" + ITEM_KEY, JsonUtils.objectToJson(item)); 3 // 设置key的过期时间 4 jed

缓存:本地缓存和分布式缓存及缓存过期时间设置

1.首先对于本地内存缓存,就是把数据缓存在本机的内存中,如下图1所示: 2. 分布式缓存机制:可能存在跨进程,跨域访问缓存数据 对于分布式的缓存,此时因为缓存的数据是放在缓存服务器中的,或者说,此时应用程序需要跨进程的去访问分布式缓存服务器,如图2: 当我们在应用中使用跨进程的缓存机制,例如分布式缓存memcached或者微软的AppFabric,此时数据被缓存在应用程序之外的进程中.每次,当我们要把一些数据缓存起来的时候,缓存的API就会把数据首先序列化为字节的形式,然后把这些字节发送给缓存服

Java 8新的时间日期库的20个使用示例

除了lambda表达式,stream以及几个小的改进之外,Java 8还引入了一套全新的时间日期API,在本篇教程中我们将通过几个简单的任务示例来学习如何使用Java 8的这套API.Java对日期,日历及时间的处理一直以来都饱受诟病,尤其是它决定将java.util.Date定义为可修改的以及将SimpleDateFormat实现成非线程安全的. 看来Java已经意识到需要为时间及日期功能提供更好的支持了,这对已经习惯使用Joda时间日期库的社区而言也是件好事.关于这个新的时间日期库的最大的优