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;
} }

  

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

  1. localstorage实现带过期时间的缓存功能

    前言 一般可以使用cookie,localstorage,sessionStorage来实现浏览器端的数据缓存,减少对服务器的请求. 1.cookie数据存放在本地硬盘中,只要在过期时间之前,都是有效 ...

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

    1. 自定义错误页虽然自定义错误页很简单,但只有少数管理员有效地利用了它.管理员可以在MMC中将HTTP错误信息映像到服务器上的绝对URL或是某个文件,更为详细的信息可以在这里找到.如果你嫌这太麻烦, ...

  3. redis 过期时间与缓存

    设置过期时间 redis对于存储的键值可以设置过期时间,对于过期了的键值,redis会自动删除. > OK > get price " > expire price (in ...

  4. Redis -- 过期时间 和 缓存 例子

    1.设置 key的生存时间,过期自动删除 exprire key  seconds    设置过期时间 秒数 ttl key   查询剩余时间 如果 设置了过期时间.对key进行 set 操作,会清除 ...

  5. 阿里面试官让我实现一个线程安全并且可以设置过期时间的LRU缓存,我蒙了!

    目录 1. LRU 缓存介绍 2. ConcurrentLinkedQueue简单介绍 3. ReadWriteLock简单介绍 4.ScheduledExecutorService 简单介绍 5. ...

  6. [置顶] 页面缓存,cache,设置缓存过期时间,OutputCache

    页面缓存 方法一: protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { //缓存有数据 if (Cach ...

  7. 利用GUID唯一标识符并设置它的过期时间

    项目中遇到一个小问题,就是把服务器创建的GUID发送给客户端,客户端接收后,下次访问时带上这个GUID, 服务端这边就进行验证,并做相应的处理. 问题是:项目中还需要这个GUID带过期时间,那是如何设 ...

  8. 可以设置过期时间的Java缓存Map

    前言 最近项目需求需要一个类似于redis可以设置过期时间的K,V存储方式.项目前期暂时不引进redis,暂时用java内存代替. 解决方案 1. ExpiringMap 功能简介 : 1.可设置Ma ...

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

    关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息 ...

随机推荐

  1. 隐藏tr

    <tr id="yinchang" style="display: none"></tr>     $("#yinchang& ...

  2. Lucene核心数据结构——FST存词典,跳表存倒排或者roarning bitmap 见另外一个文章

    Lucene实现倒排表没有使用bitmap,为了效率,lucene使用了一些策略,具体如下:1. 使用FST保存词典,FST可以实现快速的Seek,这种结构在当查询可以表达成自动机时(PrefixQu ...

  3. qt mvc3

    前面两节讲的model是一维的,这次开始二维的也就是我们常说的Table,相对与list,我们多了一个列的概念. 下面讲解一个例子.我先说明一下我们这个例子,在程序目录下,我们有一个文本文件,其中存放 ...

  4. Linux系统中rm删除命令

    rm命令 1.可以删除一个目录中的一个或多个文件或目录 2.可以将某个目录及其下属的所有文件及其子目录均删除掉 3.对于链接文件,只是删除整个链接文件,而原有文件保持不变 语法 rm (选项)(参数) ...

  5. 解决问题:OSError: mysql_config not found

    通过pip install mysqlclient时报出了OSError: mysql_config not found错误,如下 Traceback (most recent call last): ...

  6. JavaScript WebSocket 使用总结

    翻看之前写的 Highcharts使用总结  和 前后台交互之传参方式,想对 WebSocket 单独写一个使用总结. 一.认识 WebSocket . WebSocket 是 H5 新出的一种协议, ...

  7. BitmapFactory: inSampleSize 的一些思考

    一. BitmapFactory.Options 中inSampleSize的取值问题 关于inSampleSize的取值问题Google已经给出了一个推荐的算法:(https://developer ...

  8. 如何git revert merge commit?

    答: git revert -m <parent-number> <commit-id> (适用于merge操作的commit) 参考资料: https://blog.csdn ...

  9. window 下 某个端口被占用

    window 下 某个端口被占用 1. 开始—->运行—->cmd,或者是window+R组合键,调出命令窗口: 2.输入命令:netstat -ano,列出所有端口的情况.在列表中我们观 ...

  10. SQL-W3School-函数:SQL HAVING 子句

    ylbtech-SQL-W3School-函数:SQL HAVING 子句 1.返回顶部 1. HAVING 子句 在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与合计函数一起使 ...