com.github.support.cache.timeout.TimeoutCache Maven / Gradle / Ivy
package com.github.support.cache.timeout;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* 超时内存缓存,当一个对象缓存了一定的时间,不管是否被访问过,都会被移除缓存,
* 同时允许提供缓存对象数量限制,这个要看具体的业务需求,比如说本来整个业务中
* 其实就只会缓存一个对象,就没必要限制数量了
*
*
* @author Arvin
* @time 2017/05/04 10:30
*/
public class TimeoutCache {
/**
* 缓存项
*
* @param KEY
* @param Value
*/
class CacheItem implements Comparable> {
CacheItem(K2 key, V2 object, long ttl) {
this.key = key;
this.cacheItem = object;
this.ttl = ttl;
this.createTime = System.currentTimeMillis();
}
final K2 key;
final V2 cacheItem; // 缓存的对象
long createTime; // 创建时间
long ttl; // objects timeout (time-to-live), 0 = no timeout
AtomicLong accessCount = new AtomicLong(0); // 命中次数
boolean isExpired() {
return ttl != 0 && createTime + ttl < System.currentTimeMillis();
}
V2 getItem() {
accessCount.incrementAndGet(); // 命中次数加一
return cacheItem;
}
@Override
public int compareTo(CacheItem o) {
return (int) (o.createTime - this.createTime);
}
}
/**
* 当缓存数量达到上限的时候,使用什么策略移除缓存项
*/
enum PruneStrategy {
/** 先进先出 */
FIFO,
/** 按照命中率, 命中率低的将被移除 least frequently used */
LFU
}
/**
* Timeout Cache Builder
*/
public static class Builder {
/** 清空缓存的策略 */
private PruneStrategy pruneStrategy = PruneStrategy.LFU;
/** 对象数量限制,等于0表示不限制数量 */
private int limitSize = 0;
/** 默认超时时间,单位是毫秒, 如果是0表示永不超时,即一直会缓存,除非手动移除 */
private long defaultTimeout = 0;
/** 每隔多长时间检测清空一次过期缓存, 单位是毫秒, 默认30秒, 如果timeout不为0,默认设置和timeout一致 */
private long pruneTimeoutInterval = 3000;
public Builder pruneStrategy(PruneStrategy pruneStrategy) {
this.pruneStrategy = pruneStrategy;
return this;
}
public Builder limitSize(int limitSize) {
this.limitSize = limitSize;
return this;
}
public Builder defaultTimeout(long defaultTimeout) {
this.defaultTimeout = defaultTimeout;
return this;
}
public Builder pruneTimeoutInterval(long pruneTimeoutInterval) {
this.pruneTimeoutInterval = pruneTimeoutInterval;
return this;
}
public TimeoutCache build() {
return new TimeoutCache(limitSize, defaultTimeout, pruneTimeoutInterval, pruneStrategy);
}
}
/**
* 构造器
*/
public static Builder builder() {
return new Builder();
}
/** 清空缓存的策略 */
private final PruneStrategy pruneStrategy;
/** 对象数量限制,等于0表示不限制数量 */
private final int limitSize;
/** 默认超时时间,单位是毫秒, 如果是0表示永不超时,即一直会缓存,除非手动移除 */
private final long defaultTimeoutMilli;
/** 每隔多长时间检测清空一次过期缓存, 单位是毫秒, 默认30秒, 如果timeout不为0,默认设置和timeout一致 */
private final long pruneTimeoutInterval;
/** 用于缓存对象 */
private final Map> cacheMap = new HashMap>();
/** 记录每一个Key的读写锁,最大程度将锁的粒度缩小 */
private final ConcurrentHashMap lockMap = new ConcurrentHashMap();
/** 全局的读写锁 */
private final ReentrantReadWriteLock globalLock = new ReentrantReadWriteLock();
/** 定时器,用于定时清理无效对象 */
private Timer pruneTimer;
public TimeoutCache() {
this(0, 0, 30000, PruneStrategy.LFU);
}
public TimeoutCache(long defaultTimeoutMilli) {
this(0, defaultTimeoutMilli, 30000, PruneStrategy.LFU);
}
/**
* 构造函数
*
* @param limitSize 缓存数量限制, 0表示不限制
* @param defaultTimeoutMilli 默认超时时间, 单位为毫秒, 0表示永不过期
* @param pruneTimeoutInterval 每隔多少毫秒进行一次将超时的缓存项移除,defaultTimeoutMilli=0的时候默认是30秒,否则按照用户输入的值
* @param pruneStrategy 永不过期的情况下,如果缓存数量限制,默认使用什么策略移除对象
*/
public TimeoutCache(int limitSize, long defaultTimeoutMilli, long pruneTimeoutInterval, PruneStrategy pruneStrategy) {
this.limitSize = limitSize < 1 ? 0 : limitSize;
this.defaultTimeoutMilli = defaultTimeoutMilli < 1 ? 0 : defaultTimeoutMilli;
this.pruneStrategy = null == pruneStrategy ? PruneStrategy.LFU : pruneStrategy;
if (this.defaultTimeoutMilli == 0) {
this.pruneTimeoutInterval = pruneTimeoutInterval < 1 ? 30000 : pruneTimeoutInterval;
} else {
// 如果输入了pruneTimeoutInterval且大于0就使用输入的,否则使用defaultTimeout
this.pruneTimeoutInterval = pruneTimeoutInterval < 1 ? this.defaultTimeoutMilli : pruneTimeoutInterval;
}
schedulePrune(this.pruneTimeoutInterval);
}
public int getLimitSize() {
return this.limitSize;
}
public int getCacheSize() {
return this.cacheMap.values().size();
}
public long getDefaultTimeoutMilli() {
return this.defaultTimeoutMilli;
}
public long getPruneTimeoutInterval() {
return pruneTimeoutInterval;
}
/**
* Schedules prune.
*/
private void schedulePrune(long delay) {
if (pruneTimer != null) {
pruneTimer.cancel();
}
pruneTimer = new Timer();
pruneTimer.schedule(
new TimerTask() {
@Override
public void run() {
pruneCache(false);
}
}, delay, delay);
}
/**
* 判断缓存数量是否已经达到了限制
*/
private boolean isFull() {
return limitSize != 0 && limitSize <= getCacheSize();
}
/**
* 获取指定Key的lock对象,最细粒度进行并发控制
*
* @param key key
* @return 如果没有就创建一个
*/
private ReentrantReadWriteLock getLock(K key) {
ReentrantReadWriteLock lock = lockMap.get(key);
if (lock != null) {
return lock;
} else {
synchronized (lockMap) { // 双检
lock = lockMap.get(key);
if (lock != null) {
return lock;
}
lock = new ReentrantReadWriteLock();
lockMap.put(key, lock);
return lock;
}
}
}
/**
* 获取缓存中的对象
*
* @param key 缓存的KEY
*/
public V get(K key) {
ReentrantReadWriteLock.ReadLock globalReadLock = globalLock.readLock();
globalReadLock.lock(); // 具有全局读的锁才能进一步获取数据
ReentrantReadWriteLock lock = getLock(key);
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
readLock.lock();
try {
CacheItem cacheItem = cacheMap.get(key);
if (cacheItem == null) {
return null;
}
if (cacheItem.isExpired()) { // 如果过期了就返回null
cacheMap.remove(key);
return null;
}
return cacheItem.getItem();
} finally {
globalReadLock.unlock();
readLock.unlock();
}
}
public void put(K key, V value) {
put(key, value, defaultTimeoutMilli);
}
public void put(K key, V value, long timeout) {
if (isFull()) { // 如果已经满了,需要按照一定策略移除缓存对象
pruneCache(true);
}
ReentrantReadWriteLock.ReadLock globalReadLock = globalLock.readLock();
globalReadLock.lock(); // 拥有全局的读锁才能进一步put
ReentrantReadWriteLock lock = getLock(key);
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
writeLock.lock();
try {
cacheMap.put(key, new CacheItem(key, value, timeout));
} finally {
globalReadLock.unlock();
writeLock.unlock();
}
}
/**
* 从缓存中删除执行的Key
*/
public void remove(K key) {
ReentrantReadWriteLock lock = getLock(key);
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
writeLock.lock();
try {
cacheMap.remove(key);
} finally {
writeLock.unlock();
}
}
/**
* 使用指定策略移除缓存,如果defaultTimeout为0,使用过期和用户指定的策略进行移除
*/
protected void pruneCache(boolean enabledStrategy) {
ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock();
writeLock.lock();
try {
if (enabledStrategy && null != pruneStrategy) {
switch (pruneStrategy) {
case FIFO:
pruneCacheByFIFO();
break;
case LFU:
pruneCacheByLFU();
break;
}
} else {
pruneCacheByTimeout(null);
}
} finally {
writeLock.unlock();
}
}
/**
* 按照命中率移除命中率最低的一个
*/
private int pruneCacheByLFU() {
// 最少命中的缓存项
CacheItem leastAccessCacheItem = null;
Collection> cacheItems = cacheMap.values();
for (CacheItem cacheItem : cacheItems) {
if (null != cacheItem && (null == leastAccessCacheItem || leastAccessCacheItem.accessCount.get() > cacheItem.accessCount.get())) {
leastAccessCacheItem = cacheItem;
}
}
return pruneCacheByTimeout(leastAccessCacheItem);
}
/**
* 按照先进先出规则移除, 当然过期的也会被移除
*/
private int pruneCacheByFIFO() {
// 先移除第一个进来的
CacheItem firstCacheItem = null;
Collection> cacheItems = cacheMap.values();
for (CacheItem cacheItem : cacheItems) {
if (null != cacheItem && (null == firstCacheItem || firstCacheItem.createTime > cacheItem.createTime)) {
firstCacheItem = cacheItem;
}
}
return pruneCacheByTimeout(firstCacheItem);
}
/**
* 通过检测是否过期来移除缓存项
*/
private int pruneCacheByTimeout(CacheItem readyRemoveCacheItem) {
int removeCount = 0;
Iterator> values = cacheMap.values().iterator();
while (values.hasNext()) {
CacheItem cacheItem = values.next();
if (null != cacheItem && (cacheItem.equals(readyRemoveCacheItem) || cacheItem.isExpired())) {
values.remove();
++removeCount;
}
}
return removeCount;
}
/**
* 清空缓存
*/
public void clear() {
ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock();
writeLock.lock();
try {
this.cacheMap.clear();
} finally {
writeLock.unlock();
}
}
/**
* 清空匹配的缓存项
*
* @param keyPattern cache key 的匹配规则
*/
public void clear(String keyPattern) {
ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock();
writeLock.lock();
try {
if (!this.cacheMap.isEmpty()) {
List removeKeys = new ArrayList();
for (Map.Entry> entry : this.cacheMap.entrySet()) {
if (entry.getKey() instanceof String) {
if (entry.getKey().toString().matches(keyPattern)) {
removeKeys.add(entry.getKey());
}
}
}
if (!removeKeys.isEmpty()) {
for (K key : removeKeys) {
this.cacheMap.remove(key);
}
}
}
} finally {
writeLock.unlock();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy