今天是一个周六还要上班的苦逼孩子
今天遇到了一个数据,由于在Socket中会用到,使用比较频繁,但是新增的数据很少,不想每次都需要查询数据库,但是也犯不着也到缓存里(新增数据和查询不是在一个服务里面,没有集成redis)
于是想到写到内存里,由于数据表结构比较适合Map存储,于是想写到Map,但是由于新增的数据不是在一个服务里面,需要对这个Map设置失效时间,稍微提升一点数据的准确性。
这里展示的没有涉及到需要顾及线程安全的失效Map
帮助类
package com.hiqiblog.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
*
* @Description: 带有效期map 简单实现 实现了基本的方法
* @author ww
* @date: 2019-07-20
* @param <K>
* @param <V>
*/
public class ExpiryMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* default expiry time 5分钟
*/
private long EXPIRY = 1000 ;
private HashMap<K, Long> expiryMap = new HashMap<K, Long>();
public ExpiryMap(){
super();
}
public ExpiryMap(long defaultExpiryTime){
this(1 << 4, defaultExpiryTime);
}
public ExpiryMap(int initialCapacity, long defaultExpiryTime){
super(initialCapacity);
this.EXPIRY = defaultExpiryTime;
}
@Override
public V put(K key, V value) {
expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
return super.put(key, value);
}
@Override
public boolean containsKey(Object key) {
return !checkExpiry(key, true) && super.containsKey(key);
}
/**
* @param key
* @param value
* @param expiryTime 键值对有效期 毫秒
* @return
*/
public V put(K key, V value, long expiryTime) {
expiryMap.put(key, System.currentTimeMillis() + expiryTime);
return super.put(key, value);
}
@Override
public int size() {
return entrySet().size();
}
@Override
public boolean isEmpty() {
return entrySet().size() == 0;
}
@Override
public boolean containsValue(Object value) {
if (value == null){
return Boolean.FALSE;
}
Set<Entry<K, V>> set = super.entrySet();
Iterator<Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if(value.equals(entry.getValue())){
if(checkExpiry(entry.getKey(), false)) {
iterator.remove();
return Boolean.FALSE;
}else{
return Boolean.TRUE;
}
}
}
return Boolean.FALSE;
}
@Override
public Collection<V> values() {
Collection<V> values = super.values();
if(values == null || values.size() < 1){
return values;
}
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V next = iterator.next();
if(!containsValue(next)) {
iterator.remove();
}
}
return values;
}
@Override
public V get(Object key) {
if (key == null){
return null;
}
if(checkExpiry(key, true)){
return null;
}
return super.get(key);
}
/**
* @Description: 是否过期
* @return null:不存在或key为null -1:过期 存在且没过期返回value 因为过期的不是实时删除,所以稍微有点作用
* @param key
* @return
*/
public Object isInvalid(Object key) {
if (key == null){
return null;
}
if(!expiryMap.containsKey(key)){
return null;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if(flag){
super.remove(key);
expiryMap.remove(key);
return -1;
}
return super.get(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()){
expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
}
super.putAll(m);
}
@Override
public Set<Map.Entry<K,V>> entrySet() {
Set<java.util.Map.Entry<K, V>> set = super.entrySet();
Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if(checkExpiry(entry.getKey(), false)) {
iterator.remove();
}
}
return set;
}
/**
* @Description: 是否过期
**/
private boolean checkExpiry(Object key, boolean isRemoveSuper){
if(!expiryMap.containsKey(key)){
return Boolean.FALSE;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if(flag){
if(isRemoveSuper){
super.remove(key);
}
expiryMap.remove(key);
}
return flag;
}
}
dataCache.java
public class dataCache{
private ExpiryMap<String,Map> channelPropertiesMap;
public ExpiryMap<String, Map> getChannelPropertiesMap() {
if (channelPropertiesMap == null){
channelPropertiesMap = new ExpiryMap<String, Map>();
PlatformService platformService = ContextHolder.getInstance().getSpringBean("platformService");
List propertieslist = platformService.getChannelPropertieList();
if (propertieslist!=null && propertieslist.size()>0)
{
for(int i=0;i<propertieslist.size();i++){
Map obj = (Map)propertieslist.get(i);
channelPropertiesMap.put(obj.get("cid").toString(),obj);
}
}
}
return channelPropertiesMap;
}
}
platformService.java
public String getChannelProperties(int cid) {
ExpiryMap<String, Map> channelProperties = dataCache.getInstance().getChannelPropertiesMap();
Map map=channelProperties.get(String.valueOf(cid));
String result="";
if(map == null){
Object[] params = {cid};
String sql = "SELECT value FROM gms_channel_properties c where c.cid=? and c.key='wx' limit 1";
List propertieslist = getJdbcTemplate().queryForList(sql, params);
if (propertieslist!=null && propertieslist.size()>0)
{
Map obj = (Map)propertieslist.get(0);
result = obj.get("value").toString();
}
}else{
result = map.get("value").toString();
}
return result;
}
public List getChannelPropertieList() {
String sql = "SELECT c.key,c.value,c.cid FROM gms_channel_properties c";
List propertieslist = getJdbcTemplate().queryForList(sql);
return propertieslist;
}
然后研究了一下线程安全的,决定用concurrenthashmap实现
帮助类
package com.hiqiblog.util;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 实现延时过期MAP集合 支持自定义过期触发事件
*
* @Author helloc
* @Date 2019/7/20 15:12
* @Version 1.0
* @param <K>
* @param <V>
*/
public abstract class SafeExpireMap<K, V> {
protected static final Logger logger = LoggerFactory.getLogger(SafeExpireMap.class);
private long expTime = 0L;
private TimeUnit unit = null;
/**
* 线程安全的map容器
*/
ConcurrentHashMap<K, V> expireMap = null;
/**
* 控制过期时间
*/
ConcurrentHashMap<K, Long> delayMap = null;
/**
* 将map提供给外部程序操作
* @Title: getDataMap
* @Description: TODO
* @return
* @return: ConcurrentHashMap<K,V>
*/
public ConcurrentHashMap<K, V> getDataMap(){
return this.expireMap;
}
public SafeExpireMap(long expTime, TimeUnit unit) {
expireMap = new ConcurrentHashMap<K, V>();
delayMap = new ConcurrentHashMap<K, Long>();
this.expTime = expTime;
this.unit = unit;
// 启动监听线程
BaseExpireCheckTask task = new BaseExpireCheckTask(expireMap, delayMap) {
@Override
protected void expireEvent(K key,V val) {
baseExpireEvent(key,val);
}
};
task.setDaemon(false);
task.start();
}
/**
* 过期事件 子类实现
*
* @Title: baseExpireEvent
* @Description: TODO
* @param key
* @return: void
*/
protected abstract void baseExpireEvent(K key,V val);
public V put(K key, V val) {
delayMap.put(key, getExpireTime());
return expireMap.put(key, val);
}
public V remove(K key) {
return expireMap.remove(key);
}
public V get(K key){
return expireMap.get(key);
}
private Long getExpireTime() {
return unit.toMillis(expTime) + System.currentTimeMillis();
}
public static void main(String[] args) {
System.out.println(TimeUnit.SECONDS.toMinutes(120));
System.out.println(TimeUnit.MICROSECONDS.toMillis(120));
System.out.println(TimeUnit.MILLISECONDS.toMillis(120));
}
/**
* 扫描线程 定期移除过期元素并触发过期事件
*
* @ClassName: BaseExpireCheckTask
* @Description: TODO
* @author: wangs
* @date: 2017-12-25 上午9:59:18
*/
private abstract class BaseExpireCheckTask extends Thread {
ConcurrentHashMap<K, Long> delayMap = null;
ConcurrentHashMap<K, V> expireMap = null;
public BaseExpireCheckTask(ConcurrentHashMap<K, V> expireMap, ConcurrentHashMap<K, Long> delayMap) {
this.delayMap = delayMap;
this.expireMap = expireMap;
}
protected abstract void expireEvent(K key,V val);
@Override
public void run() {
Iterator<K> it = null;
K key = null;
while (true) {
if (delayMap != null && !delayMap.isEmpty()) {
it = delayMap.keySet().iterator();
while (it.hasNext()) {
key = it.next();
if (delayMap.get(key) <= System.currentTimeMillis()) {
// 元素超时
// 触发回调
expireEvent(key,expireMap.get(key));
// 移除
it.remove();
expireMap.remove(key);
delayMap.remove(key);
}
}
}
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
}
}
}