带有失效时间的Map

今天是一个周六还要上班的苦逼孩子

今天遇到了一个数据,由于在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());
                }
            }
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值