工作需要实现key带有效期的功能,小项目直接上redis有点重,就想着重写一个Map来实现该功能,网上参考下另外一位大佬的代码进行了改动,增加了closeMap()方法,保证进程能正常退出。
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* created by wangpengfei
* created in 2020/8/7
* Description: 带有效期的Map,每个key存入该map时,都需要设置key的有效期!
* 当超期后,该key失效,此外,该map加入了一个定时器,每间隔一段时间,就自动扫描该map一次,清除失效的key
* 需要进程退出需要提前调用map的closeMap()方法来关闭定时器。
*/
public class ExpiryMap <K, V> implements Map<K, V> {
private boolean isRefresh = false;
private Timer timer = null;
private static ConcurrentHashMap workMap = new ConcurrentHashMap();
private static ConcurrentHashMap<Object, Long> expiryMap = new ConcurrentHashMap<>();
public ExpiryMap() {
super();
}
public ExpiryMap(boolean isRefresh) {
this.isRefresh = isRefresh;
startClearKey();
}
public void closeMap(){
timer.cancel();
}
/**
* 定时清除失效key
*/
public void startClearKey(){
int interval = 60;//间隔时间,单位:分
int threshold = 10000;//map的容量阈值,达到该值后,将较频繁的执行key扫描工作!
timer = new Timer();
TimerTask timerTask = new TimerTask() {
int i = 0;
@Override
public void run() {
boolean isRun = ++i % interval == 0 || expiryMap.keySet().size() > threshold;
if (isRun) {
removeInValidKeys();
}
}
};
timer.schedule(timerTask, 60*1000, 60*1000); //延迟一分钟启动个,以后每个一分钟启动一次
}
private static void removeInValidKeys(){
expiryMap.keySet().forEach(key->{
if(expiryMap.get(key) < System.currentTimeMillis()){
expiryMap.remove(key);
workMap.remove(key);
}
});
System.gc();
}
/**
* put方法,需要设置key 的有效期!单位为:毫秒
* @param key
* @param value
* @param expiry key的有效期,单位:毫秒
* @return
*/
public V put(K key, V value, long expiry) {
if (!containsKey(key) || isRefresh) {//更新value,只有需要刷新时间时才需要操作expiryMap
expiryMap.put(key, System.currentTimeMillis() + expiry);
}
workMap.put(key, value);
return value;
}
@Override
public int size() {
return keySet().size();
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsKey(Object key) {
if (key!=null &&expiryMap.containsKey(key)) {
boolean flag = expiryMap.get(key) > System.currentTimeMillis();
return flag;
}
return false;
}
@Override
public boolean containsValue(Object value) {
Collection values = workMap.values();
if(values!=null){
return values.contains(value);
}
return false;
}
@Override
public V get(Object key) {
if (containsKey(key)) {
return (V) workMap.get(key);
}
return null;
}
@Deprecated
@Override
public V put(K key, V value) {
throw new RuntimeException("此方法已废弃!请加上key失效时间");
}
@Override
public V remove(Object key) {
boolean containKey=containsKey(key);
expiryMap.remove(key);
if(containKey){
return (V) workMap.remove(key);
}else{
return null;
}
}
@Deprecated
@Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new RuntimeException("此方法已废弃!");
}
@Override
public void clear() {
expiryMap.clear();
workMap.clear();
}
@Override
public Set<K> keySet() {
removeInValidKeys();
return workMap.keySet();
}
@Override
public Collection<V> values() {
removeInValidKeys();
return workMap.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
removeInValidKeys();
return workMap.entrySet();
}
public static void main(String[] args) {
//ExpiryMap emap=new ExpiryMap();
ExpiryMap emap=new ExpiryMap(true);
emap.put("key1","value1",2*1000);
emap.put("key2","value2",5*1000);
emap.put("key3","value3",15*1000);
System.out.println(emap.size());
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(emap.size());
System.out.println(emap.keySet());
System.out.println(emap.values());
System.out.println(emap.entrySet());
emap.put("key4","value4",8*1000);
System.out.println(emap.size());
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(emap.size());
System.out.println(emap.keySet());
System.out.println(emap.values());
System.out.println(emap.entrySet());
emap.closeMap();
}
}