实现背景:
由于我之前使用python写了一个比价系统的爬虫,然后没想到还真的有人叫我提供接口,所以,我打算找时间写一下java版的提供接口给调用,但是我又怕大家很不礼貌的索取,所以我需要做一个ip时间限制,首先想到redis实现,redis用途很广泛,其中有一个API我很喜欢的就是给key设置有效时间
。如果使用redis提供的 Java API 很简单。但是依赖环境也需要追加。但是我不想依赖太多,我只是想简单用一下。所以我就不想依赖redis了。最后使用Java简单实现一下。
思考:
-
需要哪些方法?
CRUD 肯定需要的,观察redis的数据结构和Map有点类似,所以我这里参考了Map接口的 API 做实现,主要提供以下方法:
- public void put(String key, Object value)
- public Object remove(String key)
- public Object get(String key)
- public boolean containsKey(String key)
- public void clear()
额外还提供以下方法进行设置
- public void setThresholdSize(int thresholdSize) // 设置阈值
- public void setOverTime(int overTime) // 设置超时毫秒值
- public void setSync(boolean sync) // 设置是否超时
-
需要注意哪些?
由于redis设置超时key的话会定时删除数据,
当然我没有去看源码,貌似redis也不是java写的,是ANSI C的吧,没记错的话。哈哈哈~~
然后设计的时候我们也需要考虑删除数据,那么如何实现超时数据进行删除呢?想到了开一个线程轮巡数据,超时就删除数据的方式,但是我觉得这样子的话也比较耗资源,不值得,如果真正想好性能优化一点的话就用redis了,所以我这里不采用线程轮序的方式,而是采用一个阈值轮巡检查数据超时情况进行移除(有可能造成一部分僵尸数据的存在,但是不会有很大影响)。除了上面的清除数据,还需要考虑的就是数据同步问题,所以这里加上关键字
volatile
,还需要考虑线程安全问题,使用了ConcurrentHashMap
和 自己定制一个线程安全的LinkedHashMap
。 -
代码如下:
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
/**
* @description: 简单定时缓存
* @author: houyu
* @create: 2018-12-05 00:02
*
* 使用:
* SimpleTimeCache<String, Object> build = SimpleTimeCache.<String, Object>builder().setOverTime(2000).build();
* build.put("A1", "A1");
* build.put("A2", "A2");
* Thread.sleep(3000);
*
* System.out.println("build.get(\"A1\") = " + build.get("A1"));
* System.out.println("build.keySet() = " + build.keySet());
* System.out.println("build.entrySet() = " + build.entrySet());
* build.forEach((k, v) -> System.out.println(k + "=>" + v));
* System.out.println("build.containsKey(\"A1\") = " + build.containsKey("A1"));
* build.put("A3", "A3");
*
* System.out.println("build.containsKey(\"A3\") = " + build.containsKey("A3"));
* build.remove("A3");
* System.out.println("build.containsKey(\"A3\") = " + build.containsKey("A3"));
*/
public class SimpleTimeCache<K, V> {
private Builder builder;
public static class Builder<K, V> {
/** 阈值 */
private int thresholdSize = 500;
/** 过时 (10秒) */
private int overTime = 10 * 1000;
public Builder<K, V> setThresholdSize(int thresholdSize) {
this.thresholdSize = thresholdSize;
return this;
}
public Builder<K, V> setOverTime(int millis) {
this.overTime = millis;
return this;
}
public SimpleTimeCache<K, V> build() {
return new SimpleTimeCache<K, V>(this);
}
// public <K, V> SimpleTimeCache<K, V> build(Class<K> keyClass, Class<V> valueClass) {
// return new SimpleTimeCache<K, V>(this);
// }
}
public static <K, V> Builder<K, V> builder() {
return new Builder<K, V>();
}
/** 最后一个添加的时间 */
private volatile long lastTime;
/** 时间Map */
private Map<K, Long> timeMap;
/** 存储值得Map */
private Map<K, V> valueMap;
protected SimpleTimeCache(Builder builder) {
this.builder = builder;
valueMap = new ConcurrentHashMap<>(this.builder.thresholdSize);
timeMap = new ConcurrentHashMap<>(this.builder.thresholdSize);
}
/** 检查并清除过时数据 */
private void checkAndCleanStaleData() {
if(System.currentTimeMillis() - lastTime > builder.overTime) {
// 最后一次添加的数据已经超时
this.clear();
} else if(timeMap.size() > builder.thresholdSize) {
// 容量达到阈值时,需要检查
valueMap.keySet().forEach(v -> {
if(System.currentTimeMillis() - timeMap.get(v) > builder.overTime) {
// 超时数据为僵尸数据,满足删除的条件
this.remove(v);
}
});
}
}
/** 新增 */
public void put(K key, V value) {
if(key == null || value == null) {
// ConcurrentHashMap 不可以存储 null 值
return;
}
lastTime = System.currentTimeMillis();
// 检查数据是否过期
this.checkAndCleanStaleData();
timeMap.put(key, System.currentTimeMillis());
valueMap.put(key, value);
}
/** 删除 */
public V remove(K key) {
timeMap.remove(key);
return valueMap.remove(key);
}
/** 获取 */
public V get(K key) {
this.checkAndCleanStaleData();
return this.getOrDefault(key, null);
}
/** 获取 */
public V getOrDefault(K key, V defaultValue) {
return (timeMap.get(key) == null || System.currentTimeMillis() - timeMap.get(key) > builder.overTime) ? null : valueMap.getOrDefault(key, defaultValue);
}
/** 判断是否存在key */
public boolean containsKey(K key) {
return this.get(key) != null;
}
public void clear() {
timeMap.clear();
valueMap.clear();
}
public Set<K> keySet() {
this.checkAndCleanStaleData();
return valueMap.keySet();
}
public Set<Map.Entry<K, V>> entrySet() {
this.checkAndCleanStaleData();
return valueMap.entrySet();
}
public void forEach(BiConsumer<? super K, ? super V> action) {
this.checkAndCleanStaleData();
valueMap.forEach(action);
}
}
使用方法在类描述中…
-
讨论
博客同步到 SHY BLOG
如果你有更好的实现,联系我分享~~邮箱:272694308@qq.com