Java实现带定时效果的Cache

原文链接

CSDN: https://blog.csdn.net/qq_35030994/article/details/80871279

修改了以下内容

  1. 将定时扫描过期Key的任务使用Spring注解@Scheduled的cron实现
  2. 将Cache改变为支持泛型,调整为单例模式。

Cache源代码

package cn.anytec.cloud.bus.utils;

import cn.anytec.cloud.bus.service.ScheduledService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Cache缓存
 * @author imyzt
 */
public class Cache<K, V> {

    private static Logger LOGGER = LoggerFactory.getLogger(Cache.class);

    /**
     * 缓存最大个数
     */
    private static final Integer CACHE_MAX_NUMBER = Constant.DEFAULT_CACHE_MAX_NUMBER;
    /**
     * 当前缓存个数
     */
    private static Integer CURRENT_SIZE = 0;
    /**
     * 缓存对象
     */
    private final Map<K, CacheObj<V>> CACHE_OBJECT_MAP = new ConcurrentHashMap<>();
    /**
     * 这个记录了缓存使用的最后一次的记录,最近使用的在最前面
     */
    private final List<K> CACHE_USE_LOG_LIST = new LinkedList<>();

    /**
     * 默认Key
     */
    private final String DEFAULT_VALUE = "DEFAULT_VALUE";

    private Cache() { }
    private static Cache cache = new Cache();

    public static Cache defaultCache() {
        return cache;
    }

    /**
     * 设置缓存
     */
    public void set(K cacheKey, V cacheValue, long cacheTime) {
        Long ttlTime = null;
        if (cacheTime <= 0L) {
            if (cacheTime == -1L) {
                ttlTime = -1L;
            } else {
                return;
            }
        }
        checkSize();
        saveCacheUseLog(cacheKey);
        CURRENT_SIZE = CURRENT_SIZE + 1;
        if (ttlTime == null) {
            ttlTime = System.currentTimeMillis() + cacheTime;
        }
        CacheObj<V> cacheObj = new CacheObj(cacheValue, ttlTime);
        CACHE_OBJECT_MAP.put(cacheKey, cacheObj);
        LOGGER.info("have set key :" + cacheKey);
    }

    /**
     * 设置缓存
     */
    public void set(K cacheKey, V cacheValue) {
        set(cacheKey, cacheValue, -1L);
    }

    /**
     * 设置缓存
     */
    public void set(K cacheKey, long cacheTime) {
        set(cacheKey, (V) DEFAULT_VALUE, cacheTime);
    }

    /**
     * 获取缓存
     */
    public Object get(K cacheKey) {
        if (checkCache(cacheKey)) {
            saveCacheUseLog(cacheKey);
            return CACHE_OBJECT_MAP.get(cacheKey).getCacheValue();
        }
        return null;
    }

    /**
     * 是否包含某一个Key值
     * @param cacheKey
     * @return
     */
    public  boolean containsKey(K cacheKey) {
        return checkCache(cacheKey);
    }

    /**
     * 删除所有缓存
     */
    public void clear() {
        LOGGER.info("have clean all key !");
        CACHE_OBJECT_MAP.clear();
        CURRENT_SIZE = 0;
    }

    /**
     * 删除某个缓存
     */
    public void del(K cacheKey) {
        Object cacheValue = CACHE_OBJECT_MAP.remove(cacheKey);
        if (cacheValue != null) {
            LOGGER.info("have delete key :" + cacheKey);
            CURRENT_SIZE = CURRENT_SIZE - 1;
        }
    }

    /**
     * 判断缓存在不在,过没过期
     */
    private boolean checkCache(K cacheKey) {
        CacheObj cacheObj = CACHE_OBJECT_MAP.get(cacheKey);
        if (cacheObj == null) {
            return false;
        }
        if (cacheObj.getTtlTime() == -1L) {
            return true;
        }
        if (cacheObj.getTtlTime() < System.currentTimeMillis()) {
            del(cacheKey);
            return false;
        }
        return true;
    }

    /**
     * 删除最近最久未使用的缓存
     */
    private void deleteLRU() {
        LOGGER.info("delete Least recently used run!");
        K cacheKey = CACHE_USE_LOG_LIST.remove(CACHE_USE_LOG_LIST.size() - 1);
        del(cacheKey);
    }

    /**
     * 删除过期的缓存
     * 禁止手动调用! 由定时任务定时拉起
     * @see ScheduledService#deleteTimeOut()
     */
    public void deleteTimeOut() {
        LOGGER.info("delete time out run!");
        List<K> deleteKeyList = new LinkedList<>();
        for(Map.Entry<K, CacheObj<V>> entry : CACHE_OBJECT_MAP.entrySet()) {
            if (entry.getValue().getTtlTime() < System.currentTimeMillis() && entry.getValue().getTtlTime() != -1L) {
                deleteKeyList.add(entry.getKey());
            }
        }

        for (K deleteKey : deleteKeyList) {
            del(deleteKey);
        }
        LOGGER.info("delete cache count is :" + deleteKeyList.size());

    }

    /**
     * 检查大小
     * 当当前大小如果已经达到最大大小
     * 首先删除过期缓存,如果过期缓存删除过后还是达到最大缓存数目
     * 删除最久未使用缓存
     */
    private  void checkSize() {
        if (CURRENT_SIZE >= CACHE_MAX_NUMBER) {
            deleteTimeOut();
        }
        if (CURRENT_SIZE >= CACHE_MAX_NUMBER) {
            deleteLRU();
        }
    }

    /**
     * 保存缓存的使用记录
     */
    private synchronized void saveCacheUseLog(K cacheKey) {
        CACHE_USE_LOG_LIST.remove(cacheKey);
        CACHE_USE_LOG_LIST.add(0, cacheKey);
    }

    public  void showUtilsInfo() {
        System.out.println("cache max count is :" + CACHE_MAX_NUMBER);
        System.out.println("cache current count is :" + CURRENT_SIZE);
        System.out.println("cache object map is :" + CACHE_OBJECT_MAP.toString());
        System.out.println("cache use log list is :" + CACHE_USE_LOG_LIST.toString());

    }

}

class CacheObj<V> {
    /**
     * 缓存对象
     */
    private V cacheValue;
    /**
     * 缓存过期时间
     */
    private Long ttlTime;

    CacheObj(V cacheValue, Long ttlTime) {
        this.cacheValue = cacheValue;
        this.ttlTime = ttlTime;
    }

    Object getCacheValue() {
        return cacheValue;
    }

    Long getTtlTime() {
        return ttlTime;
    }

    @Override
    public String toString() {
        return "CacheObj{" +
                "cacheValue=" + cacheValue +
                ", ttlTime=" + ttlTime +
                '}';
    }
}

定时任务

@Component
public class ScheduledService {

    @Scheduled(cron = "${bus.cache-delete-cycle}")
    public void deleteTimeOut() {
        Cache.defaultCache().deleteTimeOut();
    }
}