1、代码实现
@Override
public ICache<K, V> expire(K key, long timeInMills) {
long expireTime = System.currentTimeMillis() + timeInMills;
return this.expireAt(key, expireTime);
}
@Override
public ICache<K, V> expireAt(K key, long timeInMills) {
this.cacheExpire.expire(key, timeInMills);
return this;
}
为了便于处理,我们将多久之后过期,进行计算。将两个问题变成同一个问题,在什么时候过期的问题。
核心的代码,主要还是看 cacheExpire 接口。
2、过期信息的存储
/**
* 过期 map
*
* 空间换时间
* @since 0.0.3
*/
private final Map<K, Long> expireMap = new HashMap<>();
@Override
public void expire(K key, long expireAt) {
expireMap.put(key, expireAt);
}
我们定义一个 map,key 是对应的要过期的信息,value 存储的是过期时间。
3、轮询清理
/**
* 单次清空的数量限制
* @since 0.0.3
*/
private static final int LIMIT = 100;
/**
* 缓存实现
* @since 0.0.3
*/
private final ICache<K,V> cache;
/**
* 线程执行类
* @since 0.0.3
*/
private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
public CacheExpire(ICache<K, V> cache) {
this.cache = cache;
this.init();
}
/**
* 初始化任务
* @since 0.0.3
*/
private void init() {
EXECUTOR_SERVICE.scheduleAtFixedRate(new ExpireThread(), 100, 100, TimeUnit.MILLISECONDS);
}
我们固定 100ms 清理一次,每次最多清理 100 个。
4、清空任务
/**
* 定时执行任务
* @since 0.0.3
*/
private class ExpireThread implements Runnable {
@Override
public void run() {
//1.判断是否为空
if(MapUtil.isEmpty(expireMap)) {
return;
}
//2. 获取 key 进行处理
int count = 0;
for(Map.Entry<K, Long> entry : expireMap.entrySet()) {
if(count >= LIMIT) {
return;
}
expireKey(entry);
count++;
}
}
}
/**
* 执行过期操作
* @param entry 明细
* @since 0.0.3
*/
private void expireKey(Map.Entry<K, Long> entry) {
final K key = entry.getKey();
final Long expireAt = entry.getValue();
// 删除的逻辑处理
long currentTime = System.currentTimeMillis();
if(currentTime >= expireAt) {
expireMap.remove(key);
// 再移除缓存,后续可以通过惰性删除做补偿
cache.remove(key);
}
}
这个非常简单,遍历过期数据,判断对应的时间,如果已经到期了,则执行清空操作。
为了避免单次执行时间过长,最多只处理 100 条。
5、测试
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.size(3)
.build();
cache.put("1", "1");
cache.put("2", "2");
cache.expire("1", 10);
Assert.assertEquals(2, cache.size());
TimeUnit.MILLISECONDS.sleep(50);
Assert.assertEquals(1, cache.size());
System.out.println(cache.keySet());
[2]