简单即用的临时Map容器(参考TimeCacheMap和RotatingMap)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单即用的临时Map容器(参考TimeCacheMap和RotatingMap)相关的知识,希望对你有一定的参考价值。
因为业务需要,经常会缓存一些临时数据.比如:手机号发送验证码, 60s内同一个手机号不能重复发送验证码.查询航班信息,缓存1分钟热门查询数据....
之前一直使用redis作为数据缓存,简单方便..但是如果是个小App,数据没有那么大,可能需要缓存的数据只有不到100KB,使用redis就大材小用
最近一个项目上线的时候,老大跟我说:真的有必要用redis么..不行就先删掉吧..自己想了下,因为App入口有两个ip,不同机器,虽然业务量不大,为了session共享,还是上了
如果只有一个入口(不存在数据共享问题),业务量还不大,还是用临时map好一些..
参考apache的Storme项目TimeCacheMap和RotatingMap,抽取出自己方便可用的临时map容器,名字就叫RotatingCacheMap了..
基于jdk1.8,老项目估计也用不到...传送一个TimeCacheMap和RotatingMap源码分析http://www.cnblogs.com/yanghuahui/p/3677117.html
package com.tianjixie.timcross.utils; import java.time.Instant; import java.util.HashMap; /** * 临时数据容器 * 数据存储 采用 HashMap数组(回收桶) * * @author timcross */ public class RotatingCacheMap<K, V> { //默认工作Map(回收桶) 不能小于3 (为保证最小工作Map数 > 1) private static final int MIN_BUCKET_NUM = 3; //最大容量 private static final int MAXIMUM_SIZE = 10000; //默认超时(回收)时间(秒) private static final int ROTATE_TIME = 60 * 60 * 10; //默认回收检查频率(毫秒) private static final int SLEEP_TIME = 60 * 1000; //桶Map初始化容量 private final int _mapInitCapacity; //桶Map初始化扩容百分比 private final float _mapLoadFactor; //超时时间(秒) private final long _rotateTime; //桶最大索引 private final int _bucketsMaxIndex; //待删除 数据检查 方法调用(默认null,不使用数据检查) private final CheckRemoveData<K, V> _checkRemove; //所有 元素Key:元素添加时间 private final HashMap<K, Object[]> _addTime = new HashMap<K, Object[]>(); //回收桶集合 private HashMap<K, V>[] _buckets; //最后一次(桶)回收 时间 private long _lastRotate = getNowSecend(); public RotatingCacheMap() { this(MIN_BUCKET_NUM, ROTATE_TIME, SLEEP_TIME, null, 100, 0.8f); } /** * @param bucketNum 同时工作的Map(数据|回收桶)数量 默认 3 * @param rotateTime 超时时间(秒) 默认 60*60*10 (10小时) * @param sleepTime 回收线程扫描间隔(毫秒) 默认 60000 (1分钟) * @param checkRemoveDate 待删除数据检查 默认null(不使用数据检查) * @param mapInitCapacity 默认新增 数据Map 大小 * @param mapLoadFactor 默认新增 数据Map 重新散列所需百分比 */ public RotatingCacheMap(int bucketNum, long rotateTime, long sleepTime, CheckRemoveData<K, V> checkRemoveDate, int mapInitCapacity, float mapLoadFactor) { _mapLoadFactor = mapLoadFactor; _mapInitCapacity = mapInitCapacity; _checkRemove = checkRemoveDate; _rotateTime = rotateTime; _bucketsMaxIndex = bucketNum - 1; //计算 桶位置变换 时间,先进先出 long expireTime = rotateTime / bucketNum + 1; if (bucketNum < MIN_BUCKET_NUM) bucketNum = MIN_BUCKET_NUM; _buckets = new HashMap[bucketNum]; for (int i = 0; i < bucketNum; i++) { _buckets[i] = new HashMap<>(mapInitCapacity, mapLoadFactor); } //启动 数据超时 回收线程 Thread _cleaner = new Thread(() -> { while (true) { try { Thread.currentThread().sleep(sleepTime); } catch (InterruptedException ex) { } long now = getNowSecend(); //检测 桶位置变换时间 和 存储最大容量 if (now - _lastRotate > expireTime || size() > MAXIMUM_SIZE) { //启动 数据清理 new Thread(() -> rotate()).start(); _lastRotate = now; } } }); _cleaner.setDaemon(true); _cleaner.start(); } public boolean containsKey(K key) { Object[] addTime = _addTime.get(key); if (addTime == null) return false; if ((Instant.now().getEpochSecond() - (long) addTime[0]) > _rotateTime) { //数据 已超时 remove(key); return false; } return true; } public int size() { return _addTime.size(); } public V get(K key) { Object[] addTime = _addTime.get(key); if (addTime == null) return null; if ((Instant.now().getEpochSecond() - (long) addTime[0]) > _rotateTime) { //数据 已超时 remove(key); return null; } return ((HashMap<K, V>) addTime[1]).get(key); } public void put(K key, V value) { Object[] objects = new Object[2]; objects[0] = getNowSecend(); objects[1] = _buckets[0]; ((HashMap) objects[1]).put(key, value); Object[] lastPut = _addTime.get(key); _addTime.put(key, objects); if (lastPut != null && lastPut[1] != objects[1]) ((HashMap) lastPut[1]).remove(key); //检查 总数据量 是否过大 if (size() > MAXIMUM_SIZE) new Thread(() -> rotate()).start(); } public void remove(K key) { Object[] remove = _addTime.remove(key); if (remove != null) ((HashMap) remove[1]).remove(key); } public void update(K key) { Object[] update = _addTime.get(key); if (update == null) return; update[0] = getNowSecend(); HashMap<K, V> bucket = _buckets[0]; if (update[1] == bucket) return; V remove = (V) ((HashMap) update[1]).get(key); bucket.put(key, remove); ((HashMap) update[1]).remove(key); update[1] = bucket; } /** * 回收 末端桶 * 暂 禁止手动调用 * * @return 删除的Map(回收桶) */ private void rotate() { HashMap<K, V> deadMap; deadMap = _buckets[_bucketsMaxIndex]; HashMap[] hashMaps = new HashMap[_buckets.length]; hashMaps[0] = new HashMap((_mapInitCapacity + _buckets[0].size()) / 2, _mapLoadFactor); for (int i = 0; i < _bucketsMaxIndex; i++) { hashMaps[i + 1] = _buckets[i]; } deadMap.keySet().stream().forEach(key -> { Object[] remove = _addTime.remove(key); if (remove[1] != deadMap) _addTime.put(key, remove); if (_checkRemove != null) _checkRemove.check(key, deadMap.get(key)); }); //return deadMap; } /** * 获取当前时间 * * @return 秒 */ private long getNowSecend() { return Instant.now().getEpochSecond(); } /** * 数据检查接口 * * @param <K> 待删除key * @param <V> 待删除value */ public interface CheckRemoveData<K, V> { void check(K key, V val); } }
以上是关于简单即用的临时Map容器(参考TimeCacheMap和RotatingMap)的主要内容,如果未能解决你的问题,请参考以下文章