好久没有写博客了,最近在公司优化一个接口的时候打算使用一个key-value结构的本地缓存。
需要实现的功能非常简单:
1、可以控制本地缓存的最大对象数量。
2、线程安全,防止发生OOM。
3、同时支持设置单个对象的过期时间。
面对这个需求,我的选择很多,有很多框架都做的非常好,但大多数框架对我来说都太重量级了,我希望一个简单高效的实现,所以我开发了一个简单的小工具,在这里可以分享下实现思路和开发当中遇到的问题以及解决办法。
首先是key-value的结构,我底层封装了一个Map来保存数据。然后要解决线程安全问题,所以我使用了
ConcurrentHashMap这个Map的实现,关于ConcurrentHashMap这个类网上有很多介绍,我在这里就不多说了。
接下来就是需要控制最多存储的对象数量,防止本地缓存太多对象(而且对象一直都被引用,还无法被GC)造成OOM,一开始我只是简单的使用比较size和最大值来判断是否还能添加对象,但是在后来的测试发现并发量非常高的时候会多存几倍的对象,为了保证性能我还不希望加锁或使用synchronized关键字,所以我选择了AtomicInteger这个原子类巧妙的处理添加和删除方法。这个问题的解决我会在代码里详细解释。
对于过期时间实现,我参考了Redis底层对于过期部分的实现,它分为主动和被动过期,前者更节约空间后者性能更好,为此我兼容了两者的优势,采取了主动+被动的方式,在查询时判断是否过期,如果过期,清除对象同时返回null(被动)。在添加元素时判断是否还有空间,如果有正常添加,如果没有触发全量过期,之后再判断是否有空间,有就添加,没有就返回添加失败(主动)。
具体代码如下
package com.xufree.learning.java.cache;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 本地缓存
* 采用懒过期模式 在查询时才判断是否过期
* 在缓存满了的时候触发主动过期过期
*
* @author zhangmingxu ON 17:52 2019-05-20
**/
public class LocalCache {
priv