手写本地缓存, 支持过期时间, 淘汰策略

废话不多说, 直接上代码:

代码

/**
 * @author : Alvin
 * @date : 2022/06/17 10:38
 * @desc : 本地缓存
 * 淘汰策略:
 * 1. 数据超过过期时间立即失效
 * 2. 加入缓存超过最大容量时, 淘汰最不活跃(最后使用到现在时间最长)的数据.
 */
@Slf4j
public class LocalCache<K, V> implements Map<K, V> {

    /**
     * 缓存默认最大容量: 4096
     */
    private static final int DEFAULT_MAX_CAPACITY = 1 << 12;

    /**
     * 定期全面清理过期数据的间隔时间: 1分钟
     */
    private static final int ONE_MINUTE = 60 * 1000;

    /**
     * 最大容量
     */
    private int maxCapacity;

    /**
     * 默认过期时间(毫秒), -1 表示不过期.
     */
    private long defaultTtlTime = -1;

    /**
     * 缓存使用频率
     */
    private LinkedList<K> cacheList = new LinkedList<>();

    /**
     * 缓存对象
     */
    private ConcurrentHashMap<K, CacheValue<V>> cacheMap;


    public LocalCache() {
        this(DEFAULT_MAX_CAPACITY);
    }

    /**
     * @param maxCapacity 最大容量
     */
    public LocalCache(int maxCapacity) {
        this(maxCapacity, -1L);
    }


    /**
     * @param maxCapacity    最大容量
     * @param defaultTtlTime 默认过期时间(毫秒), -1 表示不过期.
     */
    public LocalCache(int maxCapacity, long defaultTtlTime) {
        this.maxCapacity = maxCapacity;
        if (defaultTtlTime > 0) {
            this.defaultTtlTime = defaultTtlTime;
        }
        cacheMap = new ConcurrentHashMap<>();
        Thread thread = new Thread(this::cleanUpExpired, "clear_thread");
        thread.setDaemon(true);
        thread.start();
    }


    /**
     * 返回缓存中有效键值的数量
     */
    @Override
    public int size() {
        return entrySet().size();
    }

    /**
     * 返回缓存中是否有 有效键值数据
     */
    @Override
    public boolean isEmpty() {
        return entrySet().size() == 0;
    }


    /**
     * 返回缓存中是否有 有效的此键
     */
    @Override
    public boolean containsKey(Object key) {
        CacheValue<V> cacheValue = cacheMap.get(key);
        if (cacheValue != null) {
            if (cacheValue.ttlTime == -1 || cacheValue.ttlTime > System.currentTimeMillis()) {
                return true;
            }
            remove(key);
            return false;
        }
        return false;
    }


    /**
     * 返回缓存中是否有 有效的此值
     */
    @Override
    public boolean containsValue(Object value) {
        long now = System.currentTimeMillis();
        for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
            CacheValue<V> cacheValue = entry.getValue();
            if (Objects.equals(cacheValue.value, value)) {
                if (cacheValue.ttlTime > now) {
                    return true;
                }
                remove(entry.getKey());
                return false;
            }
        }
        return false;
    }


    /**
     * 从缓存中获取有效的值
     */
    @Override
    public V get(Object key) {
        CacheValue<V> cacheValue = cacheMap.get(key);
        if (cacheValue != null) {
            cacheList.remove(key);
            if (cacheValue.ttlTime > System.currentTimeMillis()) {
                cacheList.addLast((K) key);
                return cacheValue.value;
            }
            cacheMap.remove(key);
        }
        return null;
    }


    /**
     * 从缓存中获取有效的值, 如果不存在, 则缓存默认值, 并返回
     *
     * @param key      键
     * @param defaultV 默认值
     */
    public V get(K key, V defaultV) {
        return get(key, defaultV, defaultTtlTime);
    }


    /**
     * 从缓存中获取有效的值, 如果不存在, 则缓存默认值, 并返回
     *
     * @param key      键
     * @param defaultV 默认值
     * @param ttlTime  缓存默认值的有效时间(毫秒)
     */
    public V get(K key, V defaultV, long ttlTime) {
        V v = get(key);
        if (v != null) {
            return v;
        }
        put(key, defaultV, ttlTime);
        return defaultV;
    }


    /**
     * 从缓存中获取有效的值, 如果不存在, 则缓存方法的返回值, 并返回
     *
     * @param key      键
     * @param function 提供值的方法
     */
    public V get(K key, Function<K, V> function) {
        return get(key, function, defaultTtlTime);
    }


    /**
     * 如果不存在, 则缓存方法的返回值, 并返回
     *
     * @param key      键
     * @param function 提供值的方法
     * @param ttlTime  缓存方法返回值的有效时间(毫秒)
     */
    public V get(K key, Function<K, V> function, long ttlTime) {
        V v = get(key);
        if (v != null) {
            return v;
        }
        V value = function.apply(key);
        put(key, value, ttlTime);
        return value;
    }


    /**
     * 缓存数据
     */
    @Override
    public V put(K key, V value) {
        return put(key, value, defaultTtlTime);
    }


    /**
     * 缓存数据
     *
     * @param key     键
     * @param value   值
     * @param ttlTime 过期时间(毫秒), -1 表示不过期.
     */
    public V put(K key, V value, long ttlTime) {
        if (ttlTime == 0) {
            return null;
        } else if (ttlTime < 0) {
            ttlTime = -1L;
        } else {
            ttlTime = System.currentTimeMillis() + ttlTime;
        }
        if (cacheMap.containsKey(key)) {
            remove(key);
        } else if (cacheList.size() >= maxCapacity) {
            cleanUpExpired();
            if (cacheList.size() >= maxCapacity) {
                synchronized (this) {
                    cacheMap.remove(cacheList.removeFirst());
                }
            }
        }
        CacheValue<V> cacheValue = new CacheValue<>(value, ttlTime);
        synchronized (this) {
            cacheList.addLast(key);
            cacheMap.put(key, cacheValue);
        }
        return value;
    }


    /**
     * 设置 key 缓存时间
     *
     * @param key     键
     * @param ttlTime 过期时间(毫秒), -1 表示不过期.
     */
    public boolean expire(K key, long ttlTime) {
        long now = System.currentTimeMillis();
        if (ttlTime < 0) {
            ttlTime = -1L;
        } else {
            ttlTime = now + ttlTime;
        }
        CacheValue<V> cacheValue = cacheMap.get(key);
        if (cacheValue != null) {
            if (cacheValue.ttlTime == -1 || cacheValue.ttlTime > now) {
                cacheValue.ttlTime = ttlTime;
                return true;
            }
            remove(key);
            return false;
        }
        return false;
    }


    /**
     * 删除缓存值
     */
    @Override
    public synchronized V remove(Object key) {
        CacheValue<V> remove = cacheMap.remove(key);
        if (remove != null) {
            cacheList.remove(key);
            return remove.value;
        }
        return null;
    }


    /**
     * 全部增加进缓存
     */
    @Override
    public synchronized void putAll(Map<? extends K, ? extends V> m) {
        if (m.size() > 0) {
            for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
                this.put(entry.getKey(), entry.getValue());
            }
        }
    }


    /**
     * 清空缓存
     */
    @Override
    public synchronized void clear() {
        cacheMap.clear();
        cacheList.clear();
    }


    /**
     * 有效值的键
     */
    @Override
    public Set<K> keySet() {
        Set<K> set = new HashSet<>();
        long now = System.currentTimeMillis();
        if (cacheMap.size() > 0) {
            for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
                if (entry.getValue().ttlTime > now) {
                    set.add(entry.getKey());
                } else {
                    remove(entry.getKey());
                }
            }
        }
        return set;
    }


    /**
     * 有效值
     */
    @Override
    public Collection<V> values() {
        ArrayList<V> vs = new ArrayList<>();
        long now = System.currentTimeMillis();
        if (cacheMap.size() > 0) {
            for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
                CacheValue<V> value = entry.getValue();
                if (value.ttlTime > now) {
                    vs.add(value.value);
                } else {
                    remove(entry.getKey());
                }
            }
        }
        return vs;
    }


    /**
     * 有效值的键值对
     */
    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> set = new LinkedHashSet<>();
        long now = System.currentTimeMillis();
        for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
            if (entry.getValue().ttlTime > now) {
                Entry<K, V> cacheEntry = new CacheEntry<>(entry.getKey(), entry.getValue().value);
                set.add(cacheEntry);
            } else {
                remove(entry.getKey());
            }
        }
        return set;
    }


    /**
     * 定期全面清理过期数据.
     */
    private void cleanUpExpired() {
        long last = System.currentTimeMillis();
        while (true) {
            long now = System.currentTimeMillis();
            while (now - ONE_MINUTE < last) {
                Thread.yield();
                now = System.currentTimeMillis();
            }
            for (Entry<K, CacheValue<V>> entry : cacheMap.entrySet()) {
                CacheValue<V> value = entry.getValue();
                if (value.ttlTime <= now) {
                    synchronized (this) {
                        cacheMap.remove(entry.getKey());
                        cacheList.remove(entry.getKey());
                    }
                }
            }
            last = now;
        }
    }


    @AllArgsConstructor
    static class CacheValue<V> {

        /**
         * 缓存对象
         */
        private V value;

        /**
         * 缓存过期时间
         */
        private long ttlTime;
    }


    @AllArgsConstructor
    static class CacheEntry<K, V> implements Map.Entry<K, V> {
        private K k;
        private V v;

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }

        @Override
        public V setValue(V value) {
            return this.v = value;
        }
    }

}

测试

public static void main(String[] args) {
        LocalCache<String, Object> cache = new LocalCache<>();
        // 缓存 java, 设置过期时间
        cache.put("我爱", "java", 1000);

        // 获取 "我爱" 的值.
        Object iLove1 = cache.get("我爱");
        System.out.println("iLove1 = " + iLove1);

        ThreadUtil.sleep(1000);
        // 再次获取 "我爱" 的值.
        Object iLove2 = cache.get("我爱");
        System.out.println("iLove2 = " + iLove2);

        // 没有缓存 "你也爱", 直接获取.
        Object uLove1 = cache.get("你也爱", (k) -> k + "java", 1000);
        System.out.println("uLove1 = " + uLove1);

        // 再次获取 "你也爱"
        Object uLove2 = cache.get("你也爱");
        System.out.println("uLove2 = " + uLove2);
    }

测试结果

iLove1 = java
iLove2 = null
uLove1 = 你也爱java
uLove2 = 你也爱java

欢迎留言交流

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值