JAVA实现延时过期MAP 支持自定义过期触发事件

如题,直接上代码:

  1 import java.util.Iterator;
  2 import java.util.concurrent.ConcurrentHashMap;
  3 import java.util.concurrent.TimeUnit;
  4
  5 import org.slf4j.Logger;
  6 import org.slf4j.LoggerFactory;
  7
  8 /**
  9  * 实现延时过期MAP集合 支持自定义过期触发事件
 10  *
 11  * @ClassName: BaseExpireMap
 12  * @Description: TODO
 13  * @author: wangs
 14  * @date: 2017-12-25 上午9:59:04
 15  * @param <K>
 16  * @param <V>
 17  */
 18 public abstract class BaseExpireMap<K, V> {
 19     protected static final Logger logger = LoggerFactory.getLogger(BaseExpireMap.class);
 20     private long expTime = 0L;
 21     private TimeUnit unit = null;
 22     /**
 23      * 线程安全的map容器
 24      */
 25     ConcurrentHashMap<K, V> expireMap = null;
 26     /**
 27      * 控制过期时间
 28      */
 29     ConcurrentHashMap<K, Long> delayMap = null;
 30
 31     /**
 32      * 将map提供给外部程序操作
 33      * @Title: getDataMap
 34      * @Description: TODO
 35      * @return
 36      * @return: ConcurrentHashMap<K,V>
 37      */
 38     public ConcurrentHashMap<K, V> getDataMap(){
 39         return this.expireMap;
 40     }
 41
 42     public BaseExpireMap(long expTime, TimeUnit unit) {
 43         expireMap = new ConcurrentHashMap<K, V>();
 44         delayMap = new ConcurrentHashMap<K, Long>();
 45         this.expTime = expTime;
 46         this.unit = unit;
 47         // 启动监听线程
 48         BaseExpireCheckTask task = new BaseExpireCheckTask(expireMap, delayMap) {
 49             @Override
 50             protected void expireEvent(K key,V val) {
 51                 baseExpireEvent(key,val);
 52             }
 53         };
 54         task.setDaemon(false);
 55         task.start();
 56     }
 57
 58     /**
 59      * 过期事件 子类实现
 60      *
 61      * @Title: baseExpireEvent
 62      * @Description: TODO
 63      * @param key
 64      * @return: void
 65      */
 66     protected abstract void baseExpireEvent(K key,V val);
 67
 68     public V put(K key, V val) {
 69         delayMap.put(key, getExpireTime());
 70         return expireMap.put(key, val);
 71     }
 72
 73     public V remove(K key) {
 74         return expireMap.remove(key);
 75     }
 76
 77     public V get(K key){
 78         return expireMap.get(key);
 79     }
 80
 81     private Long getExpireTime() {
 82         return unit.toMillis(expTime) + System.currentTimeMillis();
 83     }
 84
 85     public static void main(String[] args) {
 86         System.out.println(TimeUnit.SECONDS.toMinutes(120));
 87         System.out.println(TimeUnit.MICROSECONDS.toMillis(120));
 88         System.out.println(TimeUnit.MILLISECONDS.toMillis(120));
 89     }
 90
 91     /**
 92      * 扫描线程 定期移除过期元素并触发过期事件
 93      *
 94      * @ClassName: BaseExpireCheckTask
 95      * @Description: TODO
 96      * @author: wangs
 97      * @date: 2017-12-25 上午9:59:18
 98      */
 99     private abstract class BaseExpireCheckTask extends Thread {
100         ConcurrentHashMap<K, Long> delayMap = null;
101         ConcurrentHashMap<K, V> expireMap = null;
102
103         public BaseExpireCheckTask(ConcurrentHashMap<K, V> expireMap, ConcurrentHashMap<K, Long> delayMap) {
104             this.delayMap = delayMap;
105             this.expireMap = expireMap;
106         }
107
108         protected abstract void expireEvent(K key,V val);
109
110         public void run() {
111             Iterator<K> it = null;
112             K key = null;
113             while (true) {
114                 if (delayMap != null && !delayMap.isEmpty()) {
115                     it = delayMap.keySet().iterator();
116                     while (it.hasNext()) {
117                         key = it.next();
118                         if (delayMap.get(key) <= System.currentTimeMillis()) {// 元素超时
119                             // 触发回调
120                             expireEvent(key,expireMap.get(key));
121                             // 移除
122                             it.remove();
123                             expireMap.remove(key);
124                             delayMap.remove(key);
125                         }
126                     }
127                 }
128                 try {
129                     TimeUnit.MILLISECONDS.sleep(200);
130                 } catch (InterruptedException e) {
131                     logger.error(e.getMessage());
132                 }
133             }
134         }
135     }
136 }

上面是一个通用的延迟过期MAP容器,由两个线程安全的map集合和一个扫描线程组成,该容器会定时移除超时的元素并在移除时触发指定事件expireEvent,该方法的两个参数Key和val分别代表过期元素的键值,定义了元素过期时的触发事件,等待子类实现。

下面是一个使用实例:

 1 import java.util.concurrent.TimeUnit;
 2
 3 import com.montnets.kafka.Producer;
 4 import com.montnets.smsverify.bean.VerifyAccountSeatBean;
 5 import com.montnets.smsverify.common.StaticValues;
 6 import com.montnets.smsverify.netty.utils.GsonUtil;
 7
 8 /**
 9  * 进退坐席缓存
10  *
11  * @ClassName: SeatCache
12  * @Description: 单例
13  * @author: wangs
14  * @date: 2017-12-25 下午2:23:21
15  */
16 public class SeatCache extends BaseExpireMap<String, VerifyAccountSeatBean> {
17     private static SeatCache instance = null;
18     private static Producer producer = null;
19     static long expTime = 0L;
20     static TimeUnit unit = null;
21
22     private SeatCache(long expTime, TimeUnit unit) {
23         super(expTime, unit);
24     }
25
26     public synchronized static void init(long expTime, TimeUnit unit) {
27         SeatCache.expTime = expTime;
28         SeatCache.unit = unit;
29         if (instance == null) {
30             instance = new SeatCache(expTime, unit);
31             producer = new Producer();
32         }
33     }
34
35     public synchronized static SeatCache getInstance() {
36         if (instance == null) {
37             if (unit == null)
38                 throw (new IllegalArgumentException("please call init at first"));
39             instance = new SeatCache(expTime, unit);
40             producer = new Producer();
41         }
42         return instance;
43     }
44
45
46     /**
47      * 过期事件
48      */
49     @Override
50     protected void baseExpireEvent(String key, VerifyAccountSeatBean bean) {
51         if(bean!=null)
52             bean.setIsManual(1); //非手动退坐席
53         //更新
54         updateOffOnlie(bean);
55         //写kafka
56         send2kafka(bean);
57     }
58
59     /**
60      * 退坐席之前更新时间标记
61      * @Title: updateOffOnlie
62      * @Description: TODO
63      * @param bean
64      * @return: void
65      */
66     public static void updateOffOnlie(VerifyAccountSeatBean bean) {
67         if (bean != null) {
68             long now = System.currentTimeMillis();
69             // 退坐席时间
70             bean.setOutSeatTime(now);
71             // 在线时长
72             bean.setOnlineTime(now - bean.getInSeatTime());
73         }
74     }
75
76     /**
77      * 退坐席时将数据写入kafka
78      *
79      * @Title: send2kafka
80      * @Description: TODO
81      * @param topic
82      * @param bean
83      * @return: void
84      */
85     public static void send2kafka(VerifyAccountSeatBean bean) {
86         if (bean == null)
87             return;
88         producer.send(StaticValues.SEAT_DATA, GsonUtil.toJson(bean));
89     }
90 }

推荐一个很强大的过期缓存第三方工具包,com.google.common.cache.Cache ,它提供多种回收策略,如基于创建时间或最后一次访问时间计时回收、基于对象容量大小回收、按照命中率使用LRU算法回收等,并且一样可以自定义过期回收触发事件(写这个工具的时候我还不知道有这么个强大的玩意 - -);另外还提供命中统计的API,功能很全,可以用作数据库到前端页面中间的缓存模块,推荐使用。

世间之事莫强求,凡事太尽,缘分势必早尽。

原文地址:https://www.cnblogs.com/sen-2017/p/8639728.html

时间: 2024-08-28 10:38:59

JAVA实现延时过期MAP 支持自定义过期触发事件的相关文章

SpringCache自定义过期时间及自动刷新

背景前提 阅读说明(十分重要) 对于Cache和SpringCache原理不太清楚的朋友,可以看我之前写的文章:Springboot中的缓存Cache和CacheManager原理介绍 能关注SpringCache,想了解过期实现和自动刷新的朋友,肯定有一定Java基础的,所以先了解我的思想,达成共识比直接看代码重要许多 你可能只需要我里面其中一个点而不是原搬照抄 我在实现过程遇到三大坑,先跟大家说下,兴许对你有帮助 坑一:自己造轮子 对SpringCache不怎么了解,直接百度缓存看到Redi

JAVA中List、Map、Set的区别与选用

类层次关系如下: Collection ├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap └WeakHashMap 下面来分别介绍 Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些 Collection允许相同的元素而另一些不行.一些能排序而另一些不行.Java SDK不提供直接继承自Col

Java集合框架中Map接口的使用

在我们常用的Java集合框架接口中,除了前面说过的Collection接口以及他的根接口List接口和Set接口的使用,Map接口也是一个经常使用的接口,和Collection接口不同,Map接口并不是线性的存放对象的引用,Map接口提供了一种映射关系,所有的元素都是以键值对(Entry类型对象实例)的方式存储的,所以能够根据key快速查找value,key是映射关系的索引,value是key所指向的对象,注意,这里的value不是一个数值,而是一个对象的引用,Java集合框架的元素均是指对象!

Java Web总结十六之一自定义标签

一.自定义标签简介 1.为什么要使用自定义标签? 自定义标签主要用于移除Jsp页面中的<%java%>代码. 2.开发自定义标签的步骤: 1)开发自定义标签处理类,该类需要实现SimpleTag接口/SimpleTagSupport类,重写doTag()方法. 2)编写标签库描述符(tld)文件,在tld文件中对自定义标签进行描述,并放置在WEB-INF/目录下. 3)完成以上操作,即可在JSP页面中导入和使用自定义标签. 二.自定义标签描述 1.实现SimpleTag接口的标签通常称为简单标

[2]注解(Annotation)-- 深入理解Java:注解(Annotation)自定义注解入门

转载 http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 深入理解Java:注解(Annotation)自定义注解入门 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.J

Java里多个Map的性能比较(TreeMap、HashMap、ConcurrentSkipListMap)

问题 比较Java原生的 1.  TreeMap 2.  HashMap 3.  ConcurrentSkipListMap 3种Map的效率. 结果 模拟150W以内海量数据的插入和查找,通过增加和查找两方面的性能测试,结果如下: Map类型 插入 查找(在100W数据量中)   10W 50W 100W 150W 0-1W 0-25W 0-50W ConcurrentSkipListMap 62 ms 227 ms 433 ms 689ms 7 ms 80 ms 119 ms HashMap

Java 集合系列 08 Map架构

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和

Java:注解(Annotation)自定义注解

入理解Java:注解(Annotation)自定义注解入门 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. -------------------------------------------------------------------------------- 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 a

Android 使Volley完美支持自定义证书的Https

其实在最早的版本里,Volley甚至是不支持https协议的,只能跑http,当然你也可以自己修改他的源码让他支持,如今volley的代码经过一些改进以后, 已经可以完美支持https协议了,无论是在2.3版本以上还是在2.3版本以下,大家可以尝试用volley去访问github 是成功的,但是你如果用volley去访问 12306这种类似的 用自定义证书的网站 就很容易失败.那我下面就把volley 代码稍作修改,让volley也可以完美支持自定义证书的https请求. 当然代码只是展示功能使