[原创] java简单实现redis定时容器

实现背景:
由于我之前使用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

  • 代码如下:

package cn.util;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;

/**
 * @program: testutils
 * @description: 简单定时字典
 * @author: houyu
 * @create: 2018-12-05 00:02
 */
public class SimpleTimeMap {

    transient int      thresholdSize    = 500;                                             // 阈值      :500
    transient int      overTime         = 1000 * 10;                                       // 过时      :10秒(1000 * 10)
    transient boolean  isSync           = false;                                           // 是否同步  :不同步

    private final Lock lock             = new ReentrantLock();                             // 同步锁对象

    private volatile Map<String, Long>   timeHashMap         = new ConcurrentHashMap<>();  // 时间Map
    private volatile Map<String, Object> valueLinkedHashMap  = new LinkedHashMap<>();      // 存储Map
    private volatile Long                LastValueTime       = 0L;                         // 最后一个添加的时间

    public SimpleTimeMap() {
    }

    public SimpleTimeMap(int thresholdSize, int overTime){
        this.thresholdSize = thresholdSize;
        this.overTime = overTime;
    }

    // 同步添加
    private void putBySync(Map map, String key, Object value) {
        try {
            lock.lock();
            map.put(key, value);
        } finally {
            lock.unlock();
        }
    }

    // 同步获取
    private Object getBySync(Map map, String key) {
        try {
            lock.lock();
            return map.get(key);
        } finally {
            lock.unlock();
        }
    }

    // 检查并清除过时数据
    public void checkAndCleanStaleData() {
        if (System.currentTimeMillis() - LastValueTime > overTime) {                     // 最后一次添加的数据已经超时
            this.clear();
        }else if(timeHashMap.size() > thresholdSize){                                    // 容量达到阈值时,需要检查
            List<String> keyList = new ArrayList<>(valueLinkedHashMap.keySet());
            int size = keyList.size(); String tempKey;
            for (int i = 0; i < size; i++) {
                tempKey = keyList.get(i);
                if (System.currentTimeMillis() - timeHashMap.get(tempKey) > overTime){   // 超时数据为僵尸数据,满足删除的条件
                    this.remove(tempKey);
                }else{
                    break;
                }
            }
        }
    }

    // 新增
    public void put(String key, Object value) {
        LastValueTime = System.currentTimeMillis();
        timeHashMap.put(key, LastValueTime);
        if (isSync){
            this.putBySync(valueLinkedHashMap, key, value);
        }else{
            valueLinkedHashMap.put(key, value);
        }
        this.checkAndCleanStaleData();                                                    // 检查数据是否过期
    }

    // 删除
    public Object remove(String key) {
        timeHashMap.remove(key);
        return valueLinkedHashMap.remove(key);
    }

    // 获取
    public Object get(String key) {
        this.checkAndCleanStaleData();
        return (timeHashMap.get(key) == null || System.currentTimeMillis() - timeHashMap.get(key) > overTime) ? null : (isSync ? this.getBySync(valueLinkedHashMap, key) : valueLinkedHashMap.get(key));
    }

    public Object getOrDefault(String key, Object defaultValue) {
        Object v = this.get(key);
        return v == null ? defaultValue : v;
    }

    // 判断是否存在key
    public boolean containsKey(String key) {
        return timeHashMap.containsValue(key);
    }

    // 判断是否存在value
    public boolean containsValue(Object value) {
        return valueLinkedHashMap.containsValue(value);
    }

    public void clear() {
        timeHashMap.clear();
        valueLinkedHashMap.clear();
    }

    public Set<String> keySet() {
        return valueLinkedHashMap.keySet();
    }

    public Collection<Object> values() {
        return valueLinkedHashMap.values();

    }

    public Set<Map.Entry<String, Object>> entrySet() {
        return valueLinkedHashMap.entrySet();
    }

    public void forEach(BiConsumer<? super String, ? super Object> action) {
        valueLinkedHashMap.forEach(action);
    }

    public int getThresholdSize() {
        return thresholdSize;
    }

    public void setThresholdSize(int thresholdSize) {
        this.thresholdSize = thresholdSize;
    }

    public int getOverTime() {
        return overTime;
    }

    public void setOverTime(int overTime) {
        this.overTime = overTime;
    }

    public boolean isSync() {
        return isSync;
    }

    public void setSync(boolean sync) {
        isSync = sync;
    }

}

  • 测试如下:

    public static void main(String[] args) throws InterruptedException {
        SimpleTimeMap timeMap = new SimpleTimeMap();
        timeMap.setOverTime(1000 * 2); // 设置2秒超时
        timeMap.setThresholdSize(10);
        timeMap.setSync(false);

        timeMap.put("a", "a");
        Thread.sleep(3000); // 睡眠 3 秒
        System.out.println("获取数据a:" + timeMap.get("a"));

        System.out.println("-------我是分割线-------");

        timeMap.put("aa", "aa");
        Thread.sleep(1000); // 睡眠 3 秒
        System.out.println("获取数据aa:" + timeMap.get("aa"));
    }
  • 如果如下:

[原创] java简单实现redis定时容器_第1张图片

  • 讨论

    博客同步到 SHY BLOG
    如果你有更好的实现,联系我分享~~

    邮箱:[email protected]

你可能感兴趣的:(学习记录,java)