经常会有需要使用map进行缓存一些数据,并且希望缓存一定时间后自动过期移除该元素;有时候还希望移除元素的时候进行回调通知,以便执行相应的业务逻辑。
本文提供一种扩展Map功能的实现方法,供大家参考借鉴。实现的核心逻辑是添加key的时候同时记录时间,实例化该扩展Map的时候同时启动一个线程,以200毫秒的周期扫描Map的添加记录有效时间。比较添加时间和当前时间,过期的remove掉,同时调用回调方法。
代码实现如下:
package com.xinhui.supermap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* Map的扩展:支持key过期,以及触发回调事件
*
* @author ChenChaogui
* @Date 2024年5月18日 下午2:52:06
*
* @param <K>
* @param <V>
*/
public abstract class BaseExpireMap<K, V> {
ConcurrentHashMap<K, V> dataMap = null;
//控制过期时间
ConcurrentHashMap<K, Long> expireMap = null;
/**
*
* @param key
* @param val
* @param expireTime 过期时间 秒数
* @return
*/
public V put(K key, V val, long expireTime) {
long time = System.currentTimeMillis() + expireTime * 1000;
expireMap.put(key, time);
return dataMap.put(key, val);
}
public V remove(K key) {
expireMap.remove(key);
return dataMap.remove(key);
}
public V get(K key) {
return dataMap.get(key);
}
/**
* 过期事件 子类实现
*
* @Title: baseExpireEvent
* @param key
* @return: void
*/
protected abstract void baseExpireEvent(K key, V val);
public BaseExpireMap() {
dataMap = new ConcurrentHashMap<K, V>();
expireMap = new ConcurrentHashMap<K, Long>();
// 启动监听线程
ExpireCheckTask task = new ExpireCheckTask(dataMap, expireMap) {
@Override
protected void expireEvent(K key, V val) {
baseExpireEvent(key, val);
}
};
task.setDaemon(false);
task.start();
}
/**
* 扫描线程 定期移除过期元素并触发过期事件
*
* @author ChenChaogui
* @Date 2024年5月18日 下午2:55:41
*
*/
private abstract class ExpireCheckTask extends Thread {
ConcurrentHashMap<K, Long> expireMap = null;
ConcurrentHashMap<K, V> dataMap = null;
public ExpireCheckTask(ConcurrentHashMap<K, V> dataMap, ConcurrentHashMap<K, Long> expireMap) {
this.expireMap = expireMap;
this.dataMap = dataMap;
}
protected abstract void expireEvent(K key, V val);
public void run() {
Iterator<K> it = null;
K key = null;
while (true) {
try {
if (expireMap != null && !expireMap.isEmpty()) {
it = expireMap.keySet().iterator();
while (it.hasNext()) {
key = it.next();
if (expireMap.get(key) <= System.currentTimeMillis()) {// 元素超时
// 触发回调
expireEvent(key, dataMap.get(key));
// 移除
it.remove();
dataMap.remove(key);
expireMap.remove(key);
}
}
}
try {
//扫描周期200毫秒
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.getMessage();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
实现类进行回调方法的实现
package com.xinhui.supermap;
import java.time.LocalTime;
/**
* Map的扩展:支持key过期,以及触发回调事件
* @author ChenChaogui
* @Date 2024年5月18日 下午2:52:31
*
*/
public class ExpireMap extends BaseExpireMap<String, String> {
public ExpireMap() {
super();
}
/**
* key过期触发的回调方法
*/
@Override
protected void baseExpireEvent(String key, String value) {
System.out.println("触发事件==> 时间:" + LocalTime.now().toString()+", key:"+key+", value: " + value);
}
}
测试类
package com.xinhui.supermap;
import java.time.LocalTime;
public class ExpireMapTest {
public static ExpireMap expireMap = new ExpireMap();
public static void main(String[] args) {
System.out.println("开始时间:" + LocalTime.now().toString());
expireMap.put("key1", "value1", 5);//5s过期
expireMap.put("key2", "value2", 10);//10s过期
expireMap.put("key3", "value2", 13);//13s过期
try {
//等待10秒后添加key4
Thread.sleep(10000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("添加key4时间:" + LocalTime.now().toString());
expireMap.put("key4", "value4", 5);//5s过期
}
}
输出结果如下:
开始时间:15:21:51.529
触发事件==> 时间:15:21:56.560, key:key1, value: value1
添加key4时间:15:22:01.549
触发事件==> 时间:15:22:01.689, key:key2, value: value2
触发事件==> 时间:15:22:04.660, key:key3, value: value2
触发事件==> 时间:15:22:06.589, key:key4, value: value4
完整源代码下载:https://download.csdn.net/download/snpxchen/89363148?spm=1001.2014.3001.5501