1. 程式人生 > >JAVA實現延時過期MAP 支持自定義過期觸發事件

JAVA實現延時過期MAP 支持自定義過期觸發事件

keys 算法 public 寫入 hash pty static 實現 ssa

如題,直接上代碼:

  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,功能很全,可以用作數據庫到前端頁面中間的緩存模塊,推薦使用。

世間之事莫強求,凡事太盡,緣分勢必早盡。

JAVA實現延時過期MAP 支持自定義過期觸發事件