本文最后更新于 947 天前,其中的信息可能已经有所发展或是发生改变。
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 定期缓存,参考Redis的过期策略实现
*/
public class ExpireCache {
/**
* 默认每隔30秒随机扫描过期key
*/
public static final long DEFAULT_PERIOD_SECOND = 30;
/**
* 每次最多扫描key的数量
*/
public static final int MAX_SCAN_SIZE = 20;
/**
* 定时任务
*/
private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
/**
* 线程安全的缓存
*/
private final Map> map = new ConcurrentHashMap<>();
/**
* 创建默认过期缓存,默认每隔30秒随机删除最多20个过期的key
*/
public ExpireCache() {
this(DEFAULT_PERIOD_SECOND, TimeUnit.SECONDS);
}
/**
* 创建过期缓存,默认每隔30秒随机删除最多20个过期的key
*
* @param period 定时扫描过期key的时间间隔
* @param timeUnit 时间单位
*/
public ExpireCache(long period, TimeUnit timeUnit) {
executor.scheduleAtFixedRate(this::scanClean, period, period, timeUnit);
}
/**
* 存放一个不过期的key
*
* @param key
* @param value
*/
public void put(String key, T value) {
map.put(key, new DelayValue<>(value));
}
/**
* 存放一个过期的key
*
* @param key
* @param value
* @param delay 过期时间(单位:ms)
*/
public void put(String key, T value, long delay) {
map.put(key, new DelayValue<>(value, delay));
}
/**
* 获取一个未过期的key
* 惰性删除:若key已过期则删除并返回null
*
* @param key
*/
public T get(String key) {
DelayValue value = map.get(key);
if (Objects.isNull(value)) {
return null;
}
if (expired(value)) {
map.remove(key);
return null;
}
return value.data;
}
/**
* 随机扫描20个key,若过期则删除
*/
private void scanClean() {
List canExpiredKeys = map.entrySet().stream().filter(e -> canExpired(e.getValue())).map(Map.Entry::getKey).collect(Collectors.toList());
Collections.shuffle(canExpiredKeys);
for (int i = 0, end = Math.min(canExpiredKeys.size(), MAX_SCAN_SIZE); i < end; i++) {
String key = canExpiredKeys.get(i);
DelayValue value = map.get(key);
if (Objects.nonNull(value) && expired(value)) {
map.remove(key);
}
}
}
/**
* 判断value是否有过期时间
*/
private boolean canExpired(DelayValue value) {
return value.delay != DelayValue.FOREVER_FLAG;
}
/**
* 判断value是否过期
*/
private boolean expired(DelayValue value) {
return value.delay != DelayValue.FOREVER_FLAG && (System.currentTimeMillis() > value.timestamp + value.delay);
}
/**
* 存储过期value
*/
private static class DelayValue {
/**
* -1表示永不过期
*/
static final long FOREVER_FLAG = -1;
/**
* 存储的数据
*/
final T data;
/**
* 创建value的起始时间
*/
final long timestamp = System.currentTimeMillis();
/**
* 过期时间(单位:ms)
*/
final long delay;
DelayValue(T data) {
this.data = data;
this.delay = FOREVER_FLAG;
}
DelayValue(T data, long delay) {
this.data = data;
this.delay = delay;
}
}
}