java-单例模式实现本地缓存

几种方案:

1、分布式缓存中间件
2、前端使用 localstorage 设置浏览器缓存
3、本地缓存

优缺点

1、引入分布式缓存储存后端数据
• 优点
效率高,资源占用少,管理统一
• 难点(※※)
接入成本(需调研)
2、前端使用 浏览器缓存(localstorage)保存各种数据
• 优点
2.1、极大提高网页渲染速度,增强体验
2.2、降低后端请求数量,减小服务器压力和DB压力,增强程序运行效率
• 难点(※)
接入成本(已做过调研,还需确定完整方案)
3、后端本地缓存
• 优点
3.1、提高程序运行效率,减少DB压力
3.2、接入成本小
• 不足
占用系统内存,可能影响性能,也可能带来内存占用问题(需验证评估)

使用单例模式实现本地缓存

代码

本地缓存工具类

import jodd.madvoc.meta.In;
import java.io.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
 * @ClassName: LocalCache
 * @Description: 本地缓存
 **/
public class LocalCache {
    // 启动监控线程
    static {
        new Thread(new TimeoutTimerThread()).start();
    }
    /**
     * logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LocalCache.class);
    /**
     * 默认的缓存容量
     */
    private static int DEFAULT_CAPACITY = 512;
    /**
     * 最大容量
     */
    private static int MAX_CAPACITY = 100000;
    /**
     * 缓存永不过期
     */
    public static final Integer UN_EXPIRED_TIME = -1;
    /**
     * 保证线程安全,使用concurrentHashMap
     */
    private static final ConcurrentHashMap<String , CacheEntity> cache = new ConcurrentHashMap<String , CacheEntity>();
    /**
     * 定时清空缓存(分钟),默认10分钟
     */
    private static volatile int clearAllInterval = 1;
    private static LocalCache instance;
    // 构造方法私有化,防止实例化
    private LocalCache() {}
    // 单例模式构建对象
    public static LocalCache getInstance(){
        if (instance == null) {
            instance = new LocalCache();
        }
        return instance;
    }
    /**
     * 1、将key-value 保存到本地缓存并制定该缓存的过期时间
     *
     * @param key
     * @param value
     * @param expireTime 过期时间,如果是-1 则表示永不过期
     * @return boolean
     */
    public boolean putValue(String key, Object value, int expireTime) {
        if (key == null || "".equals(key) || value == null) {
            LoggerUtil.warn(LOGGER, "[LocalCache-putValue]-blank-key-or-empty-value: " + key);
        }
        LoggerUtil.info(LOGGER, "[LocalCache-putValue]-key: " + key);
        return putCloneValue(key, value, expireTime);
    }
    /**
     * 2、从本地缓存中获取key对应的值,如果该值不存则则返回null
     * @param key
     * @param interval 定时清理缓存的时长
     * @return Object
     */
    public Object getValue(String key, Integer interval) {
        if (interval != null) {
            clearAllInterval = interval;
        }
        boolean unCachedFlag = key == null || "".equals(key) || cache.get(key) == null;
        if (unCachedFlag) {
            LoggerUtil.warn(LOGGER, "[LocalCache-getValue]-warn-blank-key-or-empty-value");
            return null;
        }
        LoggerUtil.info(LOGGER, "[LocalCache-getValue]-getValueFromCache-Key: " + key);
        return cache.get(key).getValue();
    }
    /**
     * 3、从本地缓存中获取key对应的值,如果该值不存则则返回null
     * @param key
     * @param interval 定时清理缓存的时长
     * @param doCacheFlag 缓存开关
     * @return Object
     */
    public Object getValue(String key, Integer interval, Boolean doCacheFlag) {
        if (interval != null) {
            clearAllInterval = interval;
        }
        boolean unCachedFlag = key == null || "".equals(key) || cache.get(key) == null || (doCacheFlag != null && !doCacheFlag);
        if (unCachedFlag) {
            LoggerUtil.warn(LOGGER, "[LocalCache-getValue]-warn-blank-key-or-empty-value");
            return null;
        }
        LoggerUtil.info(LOGGER, "[LocalCache-getValue]-getValueFromCache-Key: " + key);
        return cache.get(key).getValue();
    }
    /**
     * 4、清除指定缓存
     */
    public void clearByKey(String key) {
        if (key == null || "".equals(key)) {
            LoggerUtil.warn(LOGGER, "[LocalCache-clearByKey]-warn-blank-key");
            return;
        }
        cache.remove(key);
    }
    /**
     * 5、清空所有
     */
    public void clearAll() {
        cache.clear();
    }
    /**
     * 将值通过序列化clone 处理后保存到缓存中,可以解决值引用的问题
     *
     * @param key
     * @param value
     * @param expireTime 缓存过期时间
     * @return boolean
     */
    private boolean putCloneValue(String key, Object value, int expireTime) {
        try {
            if (cache.size() >= MAX_CAPACITY) {
                LoggerUtil.warn(LOGGER, "[LocalCache-putCloneValue]-warn-缓存容量达到阈值,不再新增缓存");
                return false;
            }
            // 序列化赋值
            CacheEntity entityClone = clone(new CacheEntity(value, System.nanoTime(), expireTime));
            cache.put(key, entityClone);
            return true;
        } catch (Exception e) {
            LoggerUtil.error(LOGGER, e, "[LocalCache-putCloneValue]-error " + StackPrint.getTrace(e));
        }
        return false;
    }
    /**
     * 序列化 克隆处理
     *
     * @param object
     * @return
     */
    private <T extends Serializable> T clone(T object) {
        T cloneObject = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            oos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            cloneObject = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            LoggerUtil.error(LOGGER, e, "[LocalCache-clone]-error " + StackPrint.getTrace(e));
        }
        return cloneObject;
    }
    /**
     * 过期处理线程
     *
     */
    private static class TimeoutTimerThread implements Runnable {
        public void run() {
            while (true) {
                try {
                    LoggerUtil.info(LOGGER, "[LocalCache-TimeoutTimerThread]-start-Cache-monitor");
                    TimeUnit.MINUTES.sleep(clearAllInterval);
                    // checkTime();
                    instance.clearAll();
                } catch (Exception e) {
                    LoggerUtil.error(LOGGER, e, "[LocalCache-TimeoutTimerThread]-error " + StackPrint.getTrace(e));
                }
            }
        }
        /**
         * 过期缓存的具体处理方法
         *
         */
        private void checkTime() {
            // 开始处理过期缓存
            for (String key : cache.keySet()) {
                CacheEntity tce = cache.get(key);
                long gmtModify = tce.getGmtModify();
                // 分钟转换为纳秒
                gmtModify = gmtModify * 60000000000L;
                long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - gmtModify);
                // 过期时间 > timoutTime;
                if (tce.getExpire() > timoutTime) {
                    continue;
                }
                LoggerUtil.info(LOGGER, "[LocalCache-checkTime]-清除过期缓存: " + key);
                // 清除过期缓存和删除对应的缓存队列
                cache.remove(key);
            }
        }
    }
}

模型对象

import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
 * @ClassName: CacheEntity
 * @Description: 保存本地缓存的实体
 **/
@Setter
@Getter
public class CacheEntity implements Serializable {
    /** */
    private static final long serialVersionUID = 7172649826282703560L;
    /**
     * 值
     */
    private Object value;
    /**
     * 保存的时间戳
     */
    private long gmtModify;
    /**
     * 过期时间(分钟)
     */
    private int expire;
    public CacheEntity(Object value, long gmtModify, int expire) {
        super();
        this.value = value;
        this.gmtModify = gmtModify;
        this.expire = expire;
    }
}

调用方式

    // 清除缓存
    LocalCache.getInstance().clearByKey(bdDictionary.getEname());
    // 获取实例
    LocalCache cacheUtil = LocalCache.getInstance();
    // 通过key获取对应缓存
    cacheUtil.getValue(key, getCacheClearInterval(), doCacheFlag)

问题(todo)

代码中还有个PMD问题


image.png

Non-thread safe singletons can result in bad state changes. Eliminate static singletons if possible by instantiating the object directly. Static singletons are usually not needed as only a single instance exists anyway. Other possible fixes are to synchronize the entire method or to use an initialize-on-demand holder class. Refrain from using the double-checked locking pattern. The Java Memory Model doesn't guarantee it to work unless the variable is declared as volatile, adding an uneeded performance penalty. Reference See Effective Java, item 48.
Example:

private static Foo foo = null;
//multiple simultaneous callers may see partially initialized objects
public static Foo getFoo() {
    if (foo==null) {
        foo = new Foo();
    }
    return foo;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。