一般我们可以用HashMap做本地缓存,但是HashMap功能比较弱,不支持Key过期,不支持数据范围查找等。故在此实现了一个简易的本地缓存,取名叫fastmap。
项目地址
1.支持数据过期
2.支持等值查找
3.支持范围查找
4.支持key排序
实现思路
1.等值查找采用HashMap
2.范围查找采用TreeMap
3.数据过期实现:调用相关查询方法时清理过期Key + 定时(每秒)清理一遍过期Key
4.使用两个ReentrantReadWriteLock的读写锁实现线程安全,一个用于数据的CRUD,一个用于过期key的维护
核心代码
package com.hdwang.fastmap;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author wanghuidong
* 时间: 2022/6/26 10:10
*/
public class FastMap implements IFastMap {
/**
* 主要运用于等值查找
*/
private HashMap hashMap = new HashMap<>();
/**
* 主要运用于范围查找
*/
private TreeMap treeMap = new TreeMap<>();
/**
* 按照时间顺序保存了会过期key集合,为了实现快速删除,结构:时间戳->key列表
*/
private TreeMap> expireKeysMap = new TreeMap<>();
/**
* 保存会过期key的过期时间
*/
private HashMap keyExpireMap = new HashMap<>();
/**
* 是否启用排序(默认不启用)
*/
boolean enableSort = false;
/**
* 是否启用数据过期功能(默认启用)
*/
boolean enableExpire = true;
/**
* The comparator used to maintain order in this tree map, or
* null if it uses the natural ordering of its keys.
*
* @serial
*/
private final Comparatorsuper K> comparator;
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//数据写锁
private final Lock writeLock = readWriteLock.writeLock();
//数据读锁
private final Lock readLock = readWriteLock.readLock();
private ReentrantReadWriteLock expireKeysReadWriteLock = new ReentrantReadWriteLock();
//过期key写锁
private final Lock expireKeysWriteLock = expireKeysReadWriteLock.writeLock();
//过期key读锁
private final Lock expireKeysReadLock = expireKeysReadWriteLock.readLock();
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber() {
return nextSerialNumber.getAndIncrement();
}
/**
* 默认构造器,不启用排序
*/
public FastMap() {
this.comparator = null;
this.enableSort = false;
this.enableExpire = true;
this.init();
}
/**
* 构造器,enableExpire配置是否启用过期
*
* @param enableExpire 是否启用过期
*/
public FastMap(boolean enableExpire) {
this.comparator = null;
this.enableSort = false;
this.enableExpire = enableExpire;
this.init();
}
/**
* 构造器,enableExpire配置是否启用过期,enableSort配置是否启用排序
*
* @param enableExpire 是否启用过期
* @param enableSort 是否启用排序
*/
public FastMap(boolean enableExpire, boolean enableSort) {
this.comparator = null;
this.enableExpire = enableExpire;
this.enableSort = enableSort;
this.init();
}
/**
* 构造器,启用排序,排序器由自己传入
*
* @param comparator 排序器
*/
public FastMap(boolean enableExpire, Comparatorsuper K> comparator) {
this.enableExpire = enableExpire;
this.comparator = comparator;
this.enableSort = true;
this.init();
}
/**
* 初始化
*/
public void init() {
if (this.enableExpire) {
//启用定时器,定时删除过期key,1秒后启动,定时1秒
Timer timer = new Timer("expireTask-" + serialNumber(), true);
timer.schedule(new TimerTask() {
@Override
public void run() {
removeExpireData("timer");
}
}, 1000, 1000);
}
}
@Override
public Comparatorsuper K> comparator() {
return this.comparator;
}
@Override
public SortedMap subMap(K fromKey, K toKey) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("subMap");
try {
readLock.lock();
return this.treeMap.subMap(fromKey, toKey);
} finally {
readLock.unlock();
}
}
@Override
public SortedMap headMap(K toKey) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("headMap");
try {
readLock.lock();
return this.treeMap.headMap(toKey);
} finally {
readLock.unlock();
}
}
@Override
public SortedMap tailMap(K fromKey) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("tailMap");
try {
readLock.lock();
return this.treeMap.tailMap(fromKey);
} finally {
readLock.unlock();
}
}
@Override
public K firstKey() {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("firstKey");
try {
readLock.lock();
return this.treeMap.firstKey();
} finally {
readLock.unlock();
}
}
@Override
public K lastKey() {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("lastKey");
try {
readLock.lock();
return this.treeMap.lastKey();
} finally {
readLock.unlock();
}
}
@Override
public int size() {
//先删除过期数据
this.removeExpireData("size");
try {
readLock.lock();
return this.hashMap.size();
} finally {
readLock.unlock();
}
}
@Override
public boolean isEmpty() {
//先删除过期数据
this.removeExpireData("isEmpty");
try {
readLock.lock();
return this.hashMap.isEmpty();
} finally {
readLock.unlock();
}
}
@Override
public boolean containsKey(Object key) {
//先删除过期数据
this.removeExpireData("containsKey");
try {
readLock.lock();
return this.hashMap.containsKey(key);
} finally {
readLock.unlock();
}
}
@Override
public boolean containsValue(Object value) {
//先删除过期数据
this.removeExpireData("containsValue");
try {
readLock.lock();
return this.hashMap.containsValue(value);
} finally {
readLock.unlock();
}
}
@Override
public V get(Object key) {
//先删除过期数据
this.removeExpireData("get");
try {
readLock.lock();
return this.hashMap.get(key);
} finally {
readLock.unlock();
}
}
@Override
public V put(K key, V value) {
try {
writeLock.lock();
V val = this.hashMap.put(key, value);
if (enableSort) {
val = this.treeMap.put(key, value);
}
return val;
} finally {
writeLock.unlock();
}
}
@Override
public V remove(Object key) {
try {
writeLock.lock();
V val = this.hashMap.remove(key);
if (enableSort) {
val = this.treeMap.remove(key);
}
return val;
} finally {
writeLock.unlock();
}
}
@Override
public void putAll(Mapextends K, ? extends V> m) {
try {
writeLock.lock();
this.hashMap.putAll(m);
if (enableSort) {
this.treeMap.putAll(m);
}
} finally {
writeLock.unlock();
}
}
@Override
public void clear() {
try {
writeLock.lock();
this.hashMap.clear();
if (enableSort) {
this.treeMap.clear();
}
} finally {
writeLock.unlock();
}
}
@Override
public Set keySet() {
//先删除过期数据
this.removeExpireData("keySet");
try {
readLock.lock();
if (enableSort) {
return this.treeMap.keySet();
}
return this.hashMap.keySet();
} finally {
readLock.unlock();
}
}
@Override
public Collection values() {
//先删除过期数据
this.removeExpireData("values");
try {
readLock.lock();
if (enableSort) {
return this.treeMap.values();
}
return this.hashMap.values();
} finally {
readLock.unlock();
}
}
@Override
public Set> entrySet() {
//先删除过期数据
this.removeExpireData("entrySet");
try {
readLock.lock();
if (enableSort) {
return this.treeMap.entrySet();
}
return this.hashMap.entrySet();
} finally {
readLock.unlock();
}
}
@Override
public Long expire(K key, Long ms) {
if (!enableExpire) {
throw new RuntimeException("未启用过期功能");
}
try {
expireKeysWriteLock.lock();
//判断是否已经设置过过期时间
Long expireTime = this.keyExpireMap.get(key);
if (expireTime != null) {
//清除之前设置的过期时间
this.keyExpireMap.remove(key);
List keys = this.expireKeysMap.get(expireTime);
if (keys != null) {
keys.remove(key);
}
}
expireTime = System.currentTimeMillis() + ms;
this.keyExpireMap.put(key, expireTime);
List keys = this.expireKeysMap.get(expireTime);
if (keys == null) {
keys = new ArrayList<>();
keys.add(key);
this.expireKeysMap.put(expireTime, keys);
} else {
keys.add(key);
}
return expireTime;
} finally {
expireKeysWriteLock.unlock();
}
}
@Override
public Long ttl(K key) {
if (!enableExpire) {
throw new RuntimeException("未启用过期功能");
}
V val = this.get(key);
if (val == null) {
//数据不存在,存活时间返回null
return null;
}
try {
expireKeysReadLock.lock();
Long expireTime = this.keyExpireMap.get(key);
return expireTime - System.currentTimeMillis();
} finally {
expireKeysReadLock.unlock();
}
}
@Override
public SortedMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("subMap");
try {
readLock.lock();
return this.treeMap.subMap(fromKey, fromInclusive, toKey, toInclusive);
} finally {
readLock.unlock();
}
}
@Override
public SortedMap headMap(K toKey, boolean inclusive) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("headMap");
try {
readLock.lock();
return this.treeMap.headMap(toKey, inclusive);
} finally {
readLock.unlock();
}
}
@Override
public SortedMap tailMap(K fromKey, boolean inclusive) {
if (!enableSort) {
throw new RuntimeException("未启用排序");
}
//先删除过期数据
this.removeExpireData("tailMap");
try {
readLock.lock();
return this.treeMap.tailMap(fromKey, inclusive);
} finally {
readLock.unlock();
}
}
/**
* 删除过期的数据
*/
private void removeExpireData(String flag) {
if (!enableExpire) {
return;
}
//查找过期key
Long curTimestamp = System.currentTimeMillis();
SortedMap> expiredKeysMap;
try {
expireKeysReadLock.lock();
//过期时间在【从前至此刻】区间内的都为过期的key
expiredKeysMap = this.expireKeysMap.headMap(curTimestamp, true);
// System.out.println(String.format("thread:%s caller:%s removeExpireData【curTime=%s,expiredKeysMap=%s】", Thread.currentThread().getName(), flag, curTimestamp, expiredKeysMap));
} finally {
expireKeysReadLock.unlock();
}
//删除过期数据
List timeKeys = new ArrayList<>();
List keys = new ArrayList<>();
for (Entry> entry : expiredKeysMap.entrySet()) {
timeKeys.add(entry.getKey());
for (K key : entry.getValue()) {
this.remove(key);
keys.add(key);
}
}
//清理过期key
try {
expireKeysWriteLock.lock();
//删除过期key集合
for (Long key : timeKeys) {
this.expireKeysMap.remove(key);
}
for (K key : keys) {
//删除过期key的过期时间戳
this.keyExpireMap.remove(key);
}
} finally {
expireKeysWriteLock.unlock();
}
}
}
注意:最新完整代码以github上为准,修复了相关bug。
Original: https://www.cnblogs.com/hdwang/p/16414103.html
Author: 追极
Title: 写了一个简易的本地缓存fastmap,支持键过期和键排序等
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/587763/
转载文章受原作者版权保护。转载请注明原作者出处!